我试图使用async await在循环中的每次迭代之间设置延迟。我有一个助手睡眠功能:
const sleep = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
这是在每个循环之间正确等待:
for (let i = 0; i < 5; i++) {
console.log('waiting')
await sleep(1000)
}
但是,这不是在每个循环之间等待:
[0, 1, 2, 3, 4].forEach(async () => {
console.log('waiting')
await sleep(1000)
});
如何修改forEach代码块以表现为常规for循环块,并在for循环的每次迭代之间有延迟?
你理论上可以建立一个承诺链,使用reduce
略微更漂亮,但同样的模式也可以用forEach
完成:
[0, 1, 2, 3, 4].reduce(async (previous) => {
await previous;
console.log('waiting')
await sleep(1000)
});
但是......为什么不使用for
循环呢?
如果您更喜欢方法而不是循环(我通常只是在美学上做),您可以引入我称为async-af
的第三方模块。
除其他外,它提供了asynchronous-friendly sequential forEach:
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
AsyncAF([0, 1, 2, 3, 4]).series.forEach(async () => {
console.log('waiting');
await sleep(1000);
});
<script src="https://unpkg.com/[email protected]/index.js"></script>
当然,你也可以使用一个简单的for...of
循环:
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
(async () => {
for (const _ of [0, 1, 2, 3, 4]) {
console.log('waiting');
await sleep(1000);
}
})();
至于为什么Array.prototype.forEach
不能按照你期望的方式工作,请采用这种过度简化的实现(here's a fuller one):
const forEach = (arr, fn) => {
for (let i = 0; i < arr.length; i++) {
// nothing is awaiting this function call
fn(arr[i], i, arr);
// i is then synchronously incremented and the next function is called
}
// return undefined
};
forEach([0, 1, 2, 3, 4], async () => {
console.log('waiting');
await delay(1000);
});
如您所见,Array.prototype.forEach
在每个元素上同步调用给定的回调函数。这就是为什么你几乎立即看到所有五个waiting
日志。有关更多信息,请参阅this question。
首先,for...of
是要走的路。
但是如果你很难想要一个.forEach
方法,你可以这样做:
const sleep = ms => {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
/* Create your own async forEach */
Array.prototype.asyncForEach = async function asyncForEach(callback, ctx){
const len = this.length;
for (let i = 0; i < len; i++)
await callback.call(ctx, this[i], i, this);
};
[0, 1, 2, 3, 4].asyncForEach(async function(n){
await sleep(1000);
console.log(n);
});