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,
我需要循环只在每次超时完成后迭代一次,然后用完成后触发最后一次.
解决方法
... }).then(function (result) { console.log(result); console.log('finish'); });
会打印
[ 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被解析,> Promise.map
将执行作为参数传递的函数.引用文档,
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,并使用相应的值进行解析,并且将立即为每个值调用该函数.所以,他们都在同一时间等待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.
由于Promises是连续创建的,并且在继续之前等待分辨率,因此保证了结果的顺序.所以你的代码应该成为
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'); });
并发选项基本上限制了在创建更多承诺之前可以创建和解决的Promises数量.因此,在这种情况下,由于限制为1,它将创建第一个承诺,并且当达到限制时,它将等到创建的Promise结算,然后转到下一个Promise.
如果使用计算的整个点是引入延迟,那么我会推荐Promise.delay
,可以像这样使用
Promise.each(Object.keys(fileA),function (key) { return Promise.delay(500).then(function () { console.log(fileA[key]); }); }).then(function () { console.log('finish'); });
延迟可以透明地将Promise的已解析值链接到下一个可执行的函数,因此代码可以缩短为
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'); });
一般注意:如果您不打算在一个可操作的函数中进行任何处理,但是您只是为了跟踪进度或跟踪该过程,那么您可以更好地将它们更改为Promise.tap
.因此,您的代码将变为
Promise.each(Object.keys(fileA),500).tap(console.log); }).then(function () { // Do processing console.log('finish'); });