为什么在外部触发多次调用时,async/await不能保证执行顺序?
我正在处理如下的异步函数:
async function doWork(id) {
console.log("Start", id);
await new Promise(res => setTimeout(res, 1000));
console.log("End", id);
}
如果我按顺序调用它:
await doWork(1);
await doWork(2);
执行顺序是可预测的。
然而,如果同一个函数被外部触发(例如用户点击、事件或定时器):
doWork(1);
doWork(2);
doWork(3);
执行会变成交错执行:
Start 1
Start 2
Start 3
End 1
End 2
End 3
为什么会这样?
解决方案
这可能在Stack Overflow的某处已经有答案,因此下次值得快速搜索一下,但不管怎样,这是我的看法。
JavaScript被设计为非阻塞并发(单线程的异步执行)。如果它自动强制按顺序执行,就会阻塞执行。
async/await 不会让函数一个接一个地执行,除非你显式地 await 它们。所以当你这样做时:
doWork(1);
doWork(2);
doWork(3);
这意味着:
Start doWork(1)
Start doWork(2)
Start doWork(3)
它们都在同时运行。
且在这个函数内部:
async function doWork(id) {
console.log("Start", id);
await delay();
console.log("End", id);
}
发生的情况是 "Start" 会立即执行,await 会暂停该函数,JS继续执行下一个调用,因此你会得到
Start 1
Start 2
Start 3
然后在1 秒后:
End 1
End 2
End 3
因此在某种意义上,await 并不意味着“在调用下一个函数之前等待”,而是意味着:“暂停这个函数,但让其他一切继续执行”。
要获得顺序,你要么显式地await
await doWork(1);
await doWork(2);
await doWork(3);
手动串联它:
doWork(1)
.then(() => doWork(2))
.then(() => doWork(3));
或者你也可以使用动态列表,比如:
[1,2,3].reduce(
(p, id) => p.then(() => doWork(id)),
Promise.resolve()
);
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。