javascript - 循环遍历任务 waterfall - promises bluebird

标签 javascript node.js asynchronous promise bluebird

我希望使用 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,

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

最佳答案

  1. 在传递给Promise.map的函数对象中,需要返回一个Promise对象,这样所有的Promises都会被resolve,并且可以将resolved值的数组传递给下一个 then 函数。在您的情况下,由于您没有明确返回任何内容,因此 undefined 将默认返回,而不是 promise 。因此,带有 finish 的 thenable 函数被执行,因为 Promises.map 的 Promise 被 undefined 解析。你可以这样确认

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

    会打印

    [ undefined, undefined, undefined, undefined, undefined ]
    finish
    one
    two
    three
    four
    five
    

    所以,你的代码应该有一个像这样的return语句

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

    现在,您将看到代码按顺序打印事物,因为我们返回 Promise 对象,并且在所有 Promise 都已解决后调用带有 finish 的 thenable 函数。但它们都没有按顺序解决。如果发生这种情况,每个数字都会在指定的时间过去后打印出来。这就把我们带到了第二部分。

  2. Promise.map 将执行作为参数传递的函数,一旦数组中的 Promises 被解析。引用文档,

    The mapper function for a given item is called as soon as possible, that is, when the promise for that item's index in the input array is fulfilled.

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

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

    Iteration happens serially. .... If the iterator function returns a promise or a thenable, the result for the promise is awaited for before continuing with next iteration.

    由于 Promise 是连续创建的,并且在继续之前等待解决,因此结果的顺序是有保证的。所以你的代码应该变成

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

    补充说明:

    如果顺序无关紧要,按照 Benjamin Gruenbaum 的建议,您可以使用 Promise.map 本身,使用 concurrency limit ,像这样

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

    concurrency 选项基本上限制了在创建更多 promise 之前可以创建和解析的 promise 数量。因此,在这种情况下,由于限制为 1,它会创建第一个 Promise,当达到限制时,它会等到创建的 Promise 解决,然后再继续下一个 Promise。


如果使用 calculate 的全部目的是引入延迟,那么我会推荐 Promise.delay ,可以这样使用

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

delay 可以透明地将 Promise 的 resolved 值链接到下一个 thenable 函数,因此代码可以缩短为

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

因为 Promise.delay 接受一个动态值,你可以简单地写成相同的

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

如果 Promise 链自己到这里结束,最好使用 .done() 方法来标记它,像这样

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

一般注意事项:如果您不打算在 thenable 函数中进行任何处理,而只是使用它来跟踪进度或跟踪流程,那么您最好将它们更改为 Promise.tap .所以,你的代码会变成

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

关于javascript - 循环遍历任务 waterfall - promises bluebird,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29590078/

相关文章:

android - 如何在 Android 应用程序中使用多个 Graph API 获取 Facebook Notes 项目的评论?

JavaScript 宽高比计算器

javascript - 在javascript中使用正则表达式解析数组语法

javascript - Google Apps 脚本(日历)- 比较 2 个事件是否在同一天

node.js - 我在使用 Cloudant Lite 服务的 Bluemix 上的 Node.js Web 应用程序中收到 500 查询错误

javascript - 如何修改这个程序,我想要下一页链接

linux - 高效的 Linux 套接字(DMA/零拷贝)

javascript - Kinetic.js – 创建网格

node.js - 分配给 node.js 变量的值返回奇怪的值

javascript - Mongoose 模式对象数组