您的pSub
链都是在reduce
调用期间创建并同步运行的。为了变得连续,它们需要进入then
回调内部:
['job1','job2'].reduce((pMain,item,curIndex) => {
return pMain.then(() => {
var pSub = Promise.resolve();
for (let i = 0; i < 2; i++)
pSub = pSub.then(() => new Promise((resolve,reject) => setTimeout(reject,2000)));
return pSub;
});
},Promise.resolve())
或者在两个循环之间仅构建一条链:
['job1','job2'].reduce((promise,outerIndex) => {
return Array.from({length: 2}).reduce((promise,_,innerIndex) => {
return promise.then(() => new Promise((resolve,2000)));
},promise);
},Promise.resolve())
@jfriend当然是正确的,对于顺序任务,您只需要编写async
/ await
代码:
for (const item of ['job1','job2']) {
for (let i = 0; i < 2; i++) {
await new Promise((resolve,2000));
}
}
使用该解决方案,您还可以轻松地将try
块放置在正确的级别。
,
因此,到现在为止,您已经了解了使用.reduce()
对诺言进行序列化时做错了什么。在评论中,我对您提出了一些建议:
- 使用现代
async/await
(必要时进行转译)
- 使用已经提供异步迭代的预构建库
- 编写/使用一些经过测试的实用程序功能,而不用每次手动编写
.reduce()
循环。
如果#1或#2不切实际,我建议您制作自己的经过测试的实用程序函数,因为.reduce()
的序列化方法很容易出错,对于尚未看过代码的人来说并不总是琐碎的知道它在做什么,而一次编写和测试的适当命名的实用程序函数更易于使用和理解(一旦编写了函数),显然也使重用变得实用。
对于预构建的库,Bluebird和Async都具有这些功能(个人而言,我更喜欢Bluebird),并且自己在运行旧版JS的嵌入式项目(Raspberry Pi)中使用了Bluebird。
关于经过测试的实用程序功能,可以快速使用以下几个功能。
iterateAsync()
就像异步.forEach()
mapAsync()
就像异步.map()
reduceAsync()
就像异步.reduce()
全部将数组作为第一个参数,并将返回promise的函数作为第二个参数。这些是与ES5兼容的,但请确保Promise
可用。这是三个功能:
// iterate an array sequentially,calling a function (that returns a promise)
// on each element of the array
// The final resolved value is whatever the last call to fn(item) resolves to
// like an asynchronous .forEach()
function iterateAsync(array,fn) {
return array.reduce(function(p,item) {
return p.then(function() {
return fn(item);
});
},Promise.resolve());
}
// iterate an array sequentially,calling a function (that returns a promise)
// on each element of the array
// The final resolved value is an array of what all the fn(item) calls resolved to
// like an asynchronous .map()
function mapAsync(array,fn) {
var results = [];
return array.reduce(function(p,item) {
return p.then(function() {
return fn(item).then(function(val) {
results.push(val);
return val;
});
});
},Promise.resolve()).then(function() {
return results;
});
}
// iterate an array sequentially,calling a function fn(item,val)
// (that returns a promise) on each element of the array. Like array.reduce(),// the next fn(accumulator,item) is passed the previous resolved value and the promise
// that fn() returns should resolve to the value you want passed to the next
// link in the chain
// The final resolved value is whatever the last call to fn(item,val) resolves to
// like an asynchronous .reduce()
function reduceAsync(array,fn,initialVal) {
return array.reduce(function(p,item) {
return p.then(function(accumulator) {
return fn(accumulator,item);
});
},Promise.resolve(initialVal));
}
请注意,使用现代Javascript功能(尤其是async/await
),所有这些功能通常都比较简单,因此这些功能主要用于无法使用这些现代功能或不实用的转堆。
为了完整起见,我将补充一点,以这种方式使用.reduce()
可能不是迭代大型数组的想法。这是因为此操作是同步地预先构建承诺链p.then().then().then().then()
,其数量.then()
等于数组的长度。如果您的数组很大(长成千上万个元素),那么可能需要大量内存才能预先构建所有这些承诺并将它们链接在一起。
对于您所指的“有限环境”中的非常大的阵列,您可能希望像这样手动进行更多迭代,因为它不会预先构建任何大型结构,而只会一次使用promises:
function iterateAsync(list,fn) {
return new Promise(function(resolve,reject) {
var index = 0;
function next(val) {
if (index < list.length) {
try {
fn(list[index++]).then(next,reject);
} catch(e) {
reject(e);
}
} else {
resolve(val);
}
}
next();
});
}
,
我想有很多方法可以实现这一目标,但就我个人而言,我总是做的是创建一个返回Promise(可能会说PromiseFactory)的函数数组。
const promiseFactoryA = () => {
return new Promise(resolve => {
console.log('PromiseA started...');
setTimeout(() => {
console.log('PromiseA resolved after 300ms');
resolve();
})
},300);
}
const promiseFactories = [promiseFactoryA,promiseFactoryA];
然后,我们可以将数组传递给此函数,该函数将按顺序运行它们:
const runPromiseSequentially = promiseFactories => {
let result = Promise.resolve();
promiseFactories.forEach(
(promiseFactory) => {
result = result.then(() => promiseFactory());
},);
return result;
}
runPromiseSequentially(promiseFactories);
基本上,它的作用是在我们希望开始操作时要求PromiseFactory创建Promise。
示例REPL
但是,如果您可以使用async
和await
,则不必要。
本文链接:https://www.f2er.com/3098436.html