JavaScript 使用 setTimeout 的串行承诺

JavaScript Serial Promises with setTimeout

我正在努力构建其中包含 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) {

    });

有几个问题:

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

  • 您的代码中未调用 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());

如果您有一个 promise-creator 函数,p,并且您想要运行一系列 promise 串行,则不需要你加载一个带有承诺的数组——相反,让它成为一个普通的值数组

请注意,我在这里也没有使用 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())

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

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 会等待所有承诺完成,然后您才能使用任何值).

我只是将此作为替代方案提及,因为您提供的示例显示没有真正需要 连续执行承诺。您可能天真地简化了问题的域,但事实是否如此只有您自己知道。