我希望使用 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。
最佳答案
在传递给
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 函数。但它们都没有按顺序解决。如果发生这种情况,每个数字都会在指定的时间过去后打印出来。这就把我们带到了第二部分。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/