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

时间:2015-09-30 19:40:36

标签: javascript scope promise bluebird

这与How do I access previous promise results in a .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);
      });
    
  3. 正如你所知,选项2相当丑陋,因为我必须手动检查mapper的index参数是否与我关心的promise结果的索引相匹配。选项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来避免在这种情况下使用范围闭包?

3 个答案:

答案 0 :(得分:1)

你有一个有趣的用例,因为Promise需要在链中多个步骤的承诺的结果。对于这样一个“落后”的问题,我建议采用“后向”解决方案;在resultB之后将promiseC添加回链中:

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

理想情况下,promiseC应该会产生resultB,但现在总是可以的。

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

...
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);
});

答案 1 :(得分:0)

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

问题是,通过使用coroutinesmodern NodeJS,我们能够完全逃避嵌套和链接,并且可以以直接的同步方式编写异步代码。我们不必进行任何链接或嵌套作用域,因为所有内容都在同一范围内。

答案 2 :(得分:0)

  

这与How do I access previous promise results in a .then() chain?

类似,但不完全相同

我认为它完全一样。只需注意您的模式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?
})

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

  • Contextual state is horrible,明确的传递是麻烦的,所以我不会在这里详细介绍它们。
  • 嵌套闭包很容易,也比你的方法更好:

    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);