带有setTimeout的JavaScript Serial Promises

时间:2017-05-06 11:26:47

标签: javascript promise bluebird es6-promise

我正在使用setTimeouts构建Promise链。所有Promise都需要串联运行而不是并行运行。我正在使用Bluebird模块来实现Promise执行的串行流程。

有人可以解释一下为什么这段代码给我输出1,2,3,4而不是4,3,2,1?

var bluebirdPromise = require('bluebird');

function p1(value) {
    return new Promise(function(resolve, reject) {
       setTimeout(function(resolve) {
           console.log(value);
           resolve;
       }, value * 1000);
    });
}

var arr = [p1(4), p1(3), p1(2), p1(1)];

bluebirdPromise.reduce(arr,
    function(item, index, length) {

    }).then(function (result) {

    });

2 个答案:

答案 0 :(得分:1)

有几个问题:

  • 您拥有的console.log不依赖于先前已解决的承诺。只有超时决定何时输出才会发生。当你在"相同"创建所有四个承诺时时间,因此同时调用所有四个setTimeout调用,在确定的超时时调用它们的回调 。之后如何链接承诺并不重要......要解决这个问题,您需要在console.log回调中移动then,因为只有当链中的前一个承诺具有回调时才会执行该回调已经解决了。

  • 您的代码中未调用resolve函数。您需要添加括号。

  • setTimeout回调的resolve参数隐藏了具有相同名称的 real 函数:您需要删除该参数。

这是建议的更正。对于此代码段,我已将蓝鸟reduce替换为标准Array#reduce,但它与蓝鸟reduce的工作方式类似:



function p1(value) {
    return new Promise(function(resolve, reject) {
       setTimeout(function() { // ***
           resolve(value); // ***
       }, value * 1000);
    });
}

var arr = [p1(4), p1(3), p1(2), p1(1)];

arr.reduce(function(promise, next) {
    return promise.then(_ => next).then( value => {
        console.log(value); // ***
        return value;
    });
}, Promise.resolve());




答案 1 :(得分:0)

如果您有一个promise-creator函数p,并且想要在串行中运行一系列promises ,则无需使用promises加载数组 - 而是,让它成为正常的值数组

注意我在这里也没有使用value * 1000 - 在您的代码中,您认为必须使用计算出的setTimeout延迟人为地编排以特定顺序触发的承诺;事实并非如此。仔细观察下面代码的评估,看看我们如何在每个承诺之间延迟1秒,.then保持秩序

另请注意,此代码将在第一个承诺结算后立即开始输出 - 而不是在输出所有值之前等待所有承诺解决

const p = x =>
  new Promise(f =>
    setTimeout(f, 1e3, x))
    
const arr = [4,3,2,1]

arr.reduce((acc, x) =>
  acc.then(() => p(x)).then(console.log), Promise.resolve())

好的,所以你有这些承诺按顺序运行,但为什么呢?除非后面的步骤以某种方式依赖于前面步骤的结果,否则你没有理由想要减慢它们的速度 - 也就是说,每个promise的结果都不依赖于其他的,所以尽可能快地计算它们。但你担心订单会丢失,对吧?别担心,一切都会好的 - 我甚至会用随机的延迟来告诉你每个承诺的重要时间不是

const p = x =>
  new Promise(f =>
    setTimeout(f, 1e3 * Math.random(), x))
    
const arr = [4,3,2,1]

arr.map(p).reduce((acc, x) =>
  acc.then(() => x).then(console.log), Promise.resolve())

所以现在,所有的承诺都可以立即启动,并且输出将在第一个承诺解决后立即开始(与Promise.all不同,Promise.all将等待所有承诺在任何值可用之前完成)。

我只提到这个作为替代方案,因为您提供的示例显示没有真正的需要,以便以串行方式执行承诺。您可能已经天真地简化了问题的域名,但只有您知道是否是这种情况。