在 .then() 链中传递 Promise.join 处理程序的结果

Pass results of Promise.join handler in .then() chain

这与

相似但不完全相同

我有这样一种情况,我并行发出两个异步请求,然后是第三个异步请求,这取决于前两个请求的成功,最后将第二个异步请求的结果传递给函数回调。

截至目前,我了解如何通过两种方式执行此操作(为简洁起见省略了 .catch 语句和函数签名):

  1. 使用范围闭包(我当前的实现)

    var foo;
    Promise.join(promiseA, promiseB, function(resultsA, resultsB) {
      foo = resultsB;
      return promiseC;
    })
    .then(function() {
      // foo is accessible here
      callback(null, foo);
    });
    
  2. 使用Promise.bind,但必须使用Promise.map而不是Promise.join

    var targetIndex = 1;
    Promise.resolve(promises)
      .bind({})
      .map(function(response, index) {
        if (index === targetIndex) {
          this.foo = response;
        }
      })
      .then(function() {
        return promiseC;
      })
      .then(function() {
        // this.foo is accessible here
        callback(null, this.foo);
      });
    

如您所知,选项 2 相当丑陋,因为我必须手动检查映射器的索引参数是否与我关心的承诺结果的索引匹配。选项 1 使用范围闭包,据我所知,在大多数情况下这是不可取的(但目前看来是我的最佳选择)。

我真正想做的是:

Promise.bind({})
  .join(promiseA, promiseB, function(resultsA, resultsB) {
     this.foo = resultsB;
     return promiseC;
  })
  .then(function() {
    // I WISH this.foo WAS ACCESSIBLE HERE!
    callback(null, this.foo);
  });

有没有办法让我使用 Promise.join 而不是 Promise.map 来避免在这种情况下使用作用域闭包?

您有一个有趣的用例,因为 Promise 需要在链中返回多个步骤的承诺结果。对于这样一个"backward"的问题,我会推荐一个"backward"的解决方案;在 promiseC:

之后将 resultB 添加回链中
Promise.join(promiseA, promiseB, function(resultA, resultB) {
  return promiseC.then(function() {
    return resultB;
  });
})
.then(function(resultB) {
  callback(null, resultB);
});

理想情况下,promiseC 的结果应该是 resultB,但现在总有可能。

编辑: 请注意,我没有在这里故意使用嵌套承诺。匿名函数只用于传递值,不执行逻辑。这种方法做同样的事情:

...
return promiseC.then(function() {
  callback(null, resultB); // really not what you should be doing
});

但不鼓励,因为它添加了一层嵌套逻辑,破坏了链接的设计原则。

编辑 2: 这可以使用绑定闭包来实现,例如:

Promise.join(promiseA, promiseB).bind({})
.then(function(resultA, resultB) {
  this.resultB = resultB;
  return promiseC;
})
.then(function(resultC) {
  callback(null, this.resultB);
});

Node 支持生成器,让我们通过 Promise.coroutine:

充分利用 Bluebird 的能力
const yourFunciton = Promise.coroutine(function*(){
    // obtain other promises
    const a = yield getPromiseA(); // function that returns promiseA
    const b = yield getPromiseB(); // function that returns promiseB
    const c = yield calculatePromiseC(a, b); 
    return b; // or whatever value you want to return, or callback with
});
// call yourFunction, it returns a promise for the completion

事实是,通过使用 coroutines and modern NodeJS 我们能够完全避免嵌套和链接,并且能够以直接的同步方式编写异步代码。我们根本不需要进行任何链接或嵌套范围界定,因为所有内容都在同一范围内。

This is similar to, but not quite the same as

我觉得完全一样。请注意您的模式 Promise.join

Promise.join(promiseA, promiseB, function(resultsA, resultsB) {
    return promiseC;
}).then(function(resultsC) {
    // how to get A or B here?
})

相当于"desugared"代码

Promise.all([promiseA, promiseB])
.then(function([resultsA, resultsB]) { // ES6 destructuring syntax
    return promiseC;
}).then(function(resultsC) {
    // how to get A or B here?
})

鉴于此,我们可以一对一地应用所有解决方案。

  • ,显式透传比较麻烦,这里就不细说了。
  • 嵌套闭包很简单,而且比你的两种方法都好:

    Promise.join(promiseA, promiseB, function(resultsA, resultsB) {
        return promiseC
        .then(function() {
            return resultsB;
        });
    }).then(callback.bind(null, null), callback);
    
  • 打破链条意味着您只需使用 Promise.join 两次:

    var promiseC_ = Promise.join(promiseA, promiseB, function(resultsA, resultsB) {
        return promiseC
    });
    Promise.join(promiseC_, promiseB).then(function(_, resultsB) {
        return resultsB;
    }).then(callback.bind(null, null), callback);
    
  • async/await 是未来,如果你无论如何都使用转译器,你应该去做:

    (async function() {
        var [resultsA, resultsB] = await Promise.all([promiseA, promiseB]);
        var resultsC = await promiseC;
        return resultsB;
    }()).then(callback.bind(null, null), callback);
    
  • 但如果您还不想在 ES6 中使用转译器,您已经可以将 Bluebird 与生成器一起使用:

    Promise.coroutine(function* () {
        var [resultsA, resultsB] = yield Promise.all([promiseA, promiseB]);
        var resultsC = yield promiseC;
        return resultsB;
    })().then(callback.bind(null, null), callback);