循环任务瀑布 - 承诺蓝鸟

时间:2015-04-12 13:39:30

标签: javascript node.js asynchronous promise bluebird

我希望用蓝鸟循环完成一些任务,只需使用超时作为实验机制。 [不希望使用异步或任何其他库]

var Promise = require('bluebird');

var fileA = {
    1: 'one',
    2: 'two',
    3: 'three',
    4: 'four',
    5: 'five'
};


function calculate(key) {
    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            resolve(fileA[key]);
        }, 500);
    });
}

Promise.map(Object.keys(fileA), function (key) {
    calculate(key).then(function (res) {
        console.log(res);
    });
}).then(function () {
    console.log('finish');
});

结果是

finish,
one,
two,
three,
four,
five,

我需要循环只在每次超时完成后迭代一次,然后用完成后触发最后一次。

1 个答案:

答案 0 :(得分:15)

  1. 在传递给Promise.map的函数对象中,需要返回一个Promise对象,以便解析所有Promises,并将已解析值的数组传递给下一个{{1功能。在您的情况下,由于您未明确返回任何内容,因此默认情况下将返回then,而不是承诺。因此,当undefined的承诺通过finish s解析时,Promises.map的可执行函数将被执行。您可以像这样确认

    undefined

    会打印

    ...
    }).then(function (result) {
        console.log(result);
        console.log('finish');
    });
    

    因此,您的代码应该有[ undefined, undefined, undefined, undefined, undefined ] finish one two three four five 这样的语句

    return

    现在,您将看到代码按顺序打印事物,因为我们返回Promise对象,并且在解析了所有Promise之后调用带有Promise.map(Object.keys(fileA), function (key) { return calculate(key).then(function (res) { console.log(res); }); }).then(function () { console.log('finish'); }); 的thenable函数。但它们都没有顺序解决。如果发生这种情况,将在指定的时间过后打印每个数字。这将我们带到第二部分。

  2. 只要解析了数组中的Promise,
  3. Promise.map将执行作为参数传递的函数。引用文档,

      

    尽快调用给定项的映射器函数,也就是说,当满足输入数组中该项的索引的承诺时。

    因此,数组中的所有值都将转换为Promises,并使用相应的值进行解析,并且将立即为每个值调用该函数。所以,他们都在同一时间等待500毫秒并立即解决。这不会按顺序发生。

    由于您希望它们按顺序执行,因此您需要使用Promise.each。引用文档,

      

    迭代连续发生。 ....如果迭代器函数返回promise或者thenable,则在继续下一次迭代之前等待promise的结果。

    由于Promises是连续创建的,并且在继续之前等待分辨率,因此保证了结果的顺序。所以你的代码应该成为

    finish

    附加说明:

    如果订单无关紧要,正如Benjamin Gruenbaum所建议的那样,您可以使用Promise.each(Object.keys(fileA), function (key) { return calculate(key).then(function (res) { console.log(res); }); }).then(function () { console.log('finish'); }); 本身,concurrency limit,就像这样

    Promise.map

    Promise.map(Object.keys(fileA), function (key) { return calculate(key).then(function (res) { console.log(res); }); }, { concurrency: 1 }).then(function () { console.log('finish'); }); 选项基本上限制了在创建更多承诺之前可以创建和解决的Promises数量。因此,在这种情况下,由于限制为1,它将创建第一个承诺,当达到限制时,它将等到创建的Promise结算,然后转到下一个Promise。


  4. 如果使用concurrency的全部内容是引入延迟,那么我会推荐Promise.delay,可以像这样使用

    calculate

    Promise.each(Object.keys(fileA), function (key) { return Promise.delay(500).then(function () { console.log(fileA[key]); }); }).then(function () { console.log('finish'); }); 可以透明地将Promise的已解析值链接到下一个可执行的函数,因此代码可以缩短为

    delay

    由于Promise.delay接受动态值,因此您可以简单地将其写为

    Promise.each(Object.keys(fileA), function (key) {
        return Promise.resolve(fileA[key]).delay(500).then(console.log);
    }).then(function () {
        console.log('finish');
    });
    

    如果Promise链本身就此结束,最好使用.done()方法来标记它,就像这样

    Promise.each(Object.keys(fileA), function (key) {
        return Promise.delay(fileA[key], 500).then(console.log);
    }).then(function () {
        console.log('finish');
    });
    

    一般注意事项:如果您不打算在一个可执行的功能中进行任何处理,但是您只是为了跟踪进度或跟踪该过程,那么您可以更好地将它们更改为{ {3}}。所以,你的代码将成为

    ...
    }).done(function () {
        console.log('finish');
    });