为什么自定义async函数会阻塞?JS异步代码执行逻辑疑问
问题1:为什么初始版本的sleep没有异步执行,第二个调用要等第一个完成?
async 函数仅对函数内 await 之后的代码做异步调度,await 之前的所有代码都是同步阻塞执行的。你写的初始版本 sleep 没有用到任何 await 关键字,整个 while 死循环都是同步代码,调用时会直接卡住JS主线程,直到循环结束才会返回一个已完成状态的Promise。
执行流程实际为:
- 调用第一个
sleep(5000),主线程卡死5秒跑循环,返回已完成的Promise - 执行第一个
console.log,此时距离start已经过了5秒 - 调用第二个
sleep(5000),主线程再卡死5秒跑循环,返回已完成的Promise - 执行第二个
console.log,此时距离start已经过了10秒左右
问题2:为什么1!打印早于2!
then 回调会在所属Promise进入已完成状态后,被加入微任务队列等待执行。第一个sleep的循环先跑完,Promise先进入已完成状态,对应的then回调先进入微任务队列;第二个sleep的循环后跑完,Promise后进入已完成状态,对应的then回调后进入微任务队列。主线程的所有同步代码跑完后,会按顺序执行微任务队列的回调,所以先打印1!再打印2!。
问题3:把sleep替换为无timeout的耗时计算逻辑会得到什么结果?
如果你的耗时计算逻辑依然是跑在JS主线程的同步代码,不管你包装成async函数还是普通函数返回Promise,只要没有把计算逻辑交给Worker线程/其他异步API处理,都会和初始版本的死循环sleep表现完全一致:
- 两次sleep调用分别阻塞主线程对应计算时长
- 两个
console.log分别打印两次计算累加的时长数值 - 最后按顺序打印
1!、2!
原因是JS是单线程事件循环模型,所有同步计算任务都会占用主线程,只有定时器、IO、Worker这类运行时提供的异步API,才会把任务交给其他线程处理,不阻塞主执行栈的代码运行。
内容的提问来源于stack exchange,提问作者iacgm
相关产品推荐
相关产品推荐

