JS事件循环是否始终优先微任务队列?实测不符求解析
为什么fetch的then回调晚于setTimeout执行?
你的问题核心不是微任务优先级失效,而是fetch的回调进入微任务队列的时机依赖于一个前置宏任务,再加上代码里的10秒线程阻塞,导致宏任务队列的执行顺序发生了变化。下面拆解具体原因:
事件循环的基础规则
- 同步代码优先执行完毕后,会先清空所有微任务队列,再依次执行宏任务队列中的任务。
- 每执行完一个宏任务,都会再次清空微任务队列,再取下一个宏任务。
setTimeout与fetch的调度差异
- setTimeout的调度:调用
setTimeout(printHello, 0)后,浏览器定时器线程开始计时,虽然设置的是0ms,但浏览器有最小延迟限制(Chrome中为4ms)。你的代码里主线程被阻塞了10秒,远大于这个延迟,所以在阻塞期间,定时器就会到期,直接把printHello回调加入宏任务队列。 - fetch的调度:调用
fetch后,浏览器网络线程发起请求。当请求完成时,网络线程不会直接把then的回调加入微任务队列,而是先把一个**“响应处理宏任务”放入主线程的宏任务队列。这个宏任务的作用是:将fetch返回的Promise状态改为resolved,再把then链中的回调加入微任务队列**。也就是说,then的回调要想被执行,必须先跑完这个前置的响应处理宏任务。
你的代码执行全流程
同步代码阶段:
- 调用
setTimeout,定时器线程启动计时; - 调用
fetch,网络线程发起请求; - 执行
blockForDuration(10000),主线程被阻塞10秒; - 阻塞结束后,输出
Me first!。 - 这10秒内,定时器在第4ms就到期,
printHello进入宏任务队列;同时网络请求完成,响应处理宏任务也进入宏任务队列,但setTimeout的宏任务比fetch的响应处理宏任务更早入队(定时器到期更快)。
- 调用
事件循环阶段:
- 同步代码执行完,微任务队列为空,直接处理宏任务队列的第一个任务:执行
printHello,输出Hello; - 这个宏任务执行完毕,微任务队列仍为空,继续处理下一个宏任务:fetch的响应处理任务。执行它时,Promise被标记为resolved,
display回调被加入微任务队列; - 现在清空微任务队列,执行
display,输出Fetched data: ...。
- 同步代码执行完,微任务队列为空,直接处理宏任务队列的第一个任务:执行
为什么视频示例的顺序不同?
视频里的代码应该没有阻塞主线程,同步代码很快执行完毕。此时:
- setTimeout的回调需要等4ms才会进入宏任务队列;
- 如果fetch的请求在4ms内完成,响应处理宏任务会先进入队列。执行这个宏任务后,
then的回调进入微任务队列并被优先执行,所以顺序是Me first!→Fetched data→Hello。
内容的提问来源于stack exchange,提问作者S Y
相关产品推荐
相关产品推荐

