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

setTimeout与fetch API执行顺序疑问:实际输出为何与预期不符?

为什么fetch的回调比无延迟setTimeout晚执行?

先拆解你的代码执行的完整流程,就能明白顺序为什么是这样:

1. 同步代码阶段

首先JS引擎会先执行所有同步代码:

  • 调用fetch发起网络请求,这个请求是浏览器层面的异步操作,需要等待服务器返回响应,此时then里的回调还不会进入微任务队列。
  • 注册第一个setTimeout(..., 1000):把回调放到浏览器的延迟队列,1秒后才会转移到宏任务队列等待执行。
  • 注册第二个无延迟的setTimeout:浏览器会在同步代码执行完毕后,立刻把这个回调放到宏任务队列的队首。
  • 执行console.log("HI this is test"),输出这句话。

2. 同步代码执行完毕后的事件循环

事件循环的规则是:先清空所有微任务队列,再执行宏任务队列里的任务,但这里有个关键:
此时fetch的网络请求还没完成(哪怕只需要几十毫秒),所以微任务队列是空的。

于是引擎开始执行宏任务队列里的第一个任务:无延迟的setTimeout回调,输出Callback with no delay。

3. 宏任务执行后的微任务检查

当这个宏任务执行完,引擎再次检查微任务队列——这时候fetch的请求已经拿到了响应,fetch返回的Promise状态变为resolved,对应的then回调被加入微任务队列:

  • 第一个then把响应转成json(这又返回一个Promise),第二个then的回调接着进入微任务队列。
  • 引擎依次执行这些微任务,最终输出{userId: 1, id: 1, title: 'delectus aut autem', completed: false}。

4. 延迟的setTimeout执行

等待1秒后,第一个setTimeout的回调被移到宏任务队列,引擎执行它,输出Callback with delay。

你之前认知的误区

你以为fetch的Promise回调会立刻进入微任务队列,但实际上:

  • 普通的Promise.resolve()会立刻把回调加入微任务队列,但fetch的Promise是依赖网络请求的异步结果的,只有当浏览器拿到服务器响应后,才会把then回调加入微任务队列。
  • 无延迟的setTimeout不需要等待网络,同步代码一结束就进入宏任务队列,自然会先于还没完成的fetch回调执行。

如果把fetch换成一个立刻resolve的Promise,比如:

Promise.resolve('test').then(res => console.log(res))

setTimeout(()=>{
  console.log("Callback with no delay")
})

console.log("HI this is test")

输出顺序就会是:

HI this is test
test
Callback with no delay

这才符合你最初对微任务优先的认知。

内容的提问来源于stack exchange,提问作者Cool Coder

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 13:55:30