You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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的回调要想被执行,必须先跑完这个前置的响应处理宏任务。

你的代码执行全流程

  1. 同步代码阶段:

    • 调用setTimeout,定时器线程启动计时;
    • 调用fetch,网络线程发起请求;
    • 执行blockForDuration(10000),主线程被阻塞10秒;
    • 阻塞结束后,输出Me first!。
    • 这10秒内,定时器在第4ms就到期,printHello进入宏任务队列;同时网络请求完成,响应处理宏任务也进入宏任务队列,但setTimeout的宏任务比fetch的响应处理宏任务更早入队(定时器到期更快)。
  2. 事件循环阶段:

    • 同步代码执行完,微任务队列为空,直接处理宏任务队列的第一个任务:执行printHello,输出Hello;
    • 这个宏任务执行完毕,微任务队列仍为空,继续处理下一个宏任务:fetch的响应处理任务。执行它时,Promise被标记为resolved,display回调被加入微任务队列;
    • 现在清空微任务队列,执行display,输出Fetched data: ...。

为什么视频示例的顺序不同?

视频里的代码应该没有阻塞主线程,同步代码很快执行完毕。此时:

  • setTimeout的回调需要等4ms才会进入宏任务队列;
  • 如果fetch的请求在4ms内完成,响应处理宏任务会先进入队列。执行这个宏任务后,then的回调进入微任务队列并被优先执行,所以顺序是Me first! → Fetched data → Hello。

内容的提问来源于stack exchange,提问作者S Y

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 17:34:59