如何在没有 "indentation pyramids" 的情况下正确表达任意 Promise 链?

How to correctly express arbitrary Promise chains without "indentation pyramids"?

Q.reduceQ.all 等方法可以帮助在异类 promise 集合的特定情况下展平 promise 链。不过请注意,一般情况:

const F = (x) => x;
const a = F(1);
const b = F(2);
const c = F(a + b);
const d = F(a + c);
const e = F(b + c);
console.log(e); 

也就是说,一系列赋值,每个项都依赖于先前定义的任意项。假设 F 是一个异步调用:

const F = (x) => Q.delay(1000).return(x);

如果不生成缩进金字塔,我无法表达该模式:

F(100).then(a =>
  F(200).then(b =>
    F(a+b).then(c =>
      F(a+c).then(d =>
        F(b+c).then(e =>
          F(d+e).then(f =>
            console.log(f)
          )
        )
      )
    )
  )
);

请注意,使用返回值是行不通的:

F(100).then(a => F(200))
    .then(b => F(a+b))
    .then(c => F(a+c))
    .then(d => F(b+c))
    .then(e => F(d+e))
    .then(f => console.log(f));

因为,例如,a 不在第二行的范围内。处理这种情况的正确方法是什么?

由于后续操作依赖于先前操作的多个位,您的选择是:

  1. 做你做过的事

  2. 把变量放到链外,边走边赋值

  3. 让整个对象绕过一个带有 ab 等属性的对象

#1 是我会坚持的,除非 真的 有很好的理由去做其他两个。幸运的是,这种积累很少像你的问题那样深入。


async/await 是几年后在这里做的方法(并且可能是在问题发布时,the proposal was finished and they could be used with a transpiler), see 他们如何简化它。这是一个工作示例:

const F = x => {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(x);
        }, 100);
    });
};
(async () => {
    const a = await F(100);
    const b = await F(200);
    const c = await F(a+b);
    const d = await F(a+c);
    const e = await F(b+c);
    const f = await F(d+e);
    console.log(f);
})()
.catch(error => {
    // ...handle/report error...
});

Live on Babel's REPL 对于环境过时的人。

第二次尝试。 JS 聊天中的@Luggage 建议使用 Async/Await 来保持参数的范围。

let a = await F(200);
let b = await F(a + 100);
let c = await F(a + b);
//... etc, all inside an async function

您也可以使用 Promise.all 或者(这是我的经验)我已经使用 async 库来帮助解决这些问题。

async.waterfall([
   (callback) => {
        let a = F(200);
        callback(null, a)
    }, (a, callback) => {
        let b = F(a+b);
        callback(null, b);
    }, //etc down the chain
]);

我认为 Promise.all 会比异步库更好地管理它,但是 async/await 是这里最漂亮的方法,尽管它需要 ES2017 support/transpiling.

Async/await 我认为很好地解决了这个问题;

async function runAsync(){
  const F = async (x) => x;
  const a = await F(1);
  const b = await F(2);
  const c = await F(a + b);
  const d = await F(a + c);
  const e = await F(b + c);
  console.log(e);
}

function runSync(){
  const F = (x) => x;
  const a = F(1);
  const b = F(2);
  const c = F(a + b);
  const d = F(a + c);
  const e = F(b + c);
  console.log(e);
}

runSync(); //5
runAsync(); //5

这将 运行 本地使用 node --harmony-async-await example

节点 7

不幸的是,您可能需要向下编译以供一般使用,并且输出可能会变得非常大。