XHR回调属宏任务,Fetch.then回调属微任务;前者响应后入宏队列、按事件循环逐个执行,后者响应解析后入微队列、当轮同步后立即批量执行。

XHR 和 Fetch 的回调入队机制本质不同:不是“谁更快”,而是“归哪类任务、何时执行”。关键差异在于浏览器如何把响应处理逻辑放进事件循环队列。
XHR 的 onload 回调属于宏任务
XMLHttpRequest 的 onload、onerror、onreadystatechange(readyState === 4 时) 等事件处理器,在响应就绪后由浏览器推入宏任务队列。这意味着:
- 它必须等当前同步代码 + 所有微任务执行完,才轮到它被事件循环取出执行
- 每次事件循环只取一个宏任务,所以多个 XHR 请求完成,它们的 onload 不会“扎堆”执行,而是依次排队
- 即使两个 XHR 同时返回,它们的 onload 也会分属两个不同的宏任务周期
Fetch 的 then 回调属于微任务
fetch() 返回 Promise,其 .then()、.catch()、.finally() 中的回调函数,在响应解析完成(如 response.json() 解析完毕)后,被推入微任务队列。因此:
- 它在本轮同步代码结束后、下一个宏任务开始前,被一次性全部清空执行
- 如果连续触发多个 fetch.then,它们会在同一轮微任务阶段集中执行,顺序严格按注册先后
- 哪怕 fetch 响应比 XHR 还晚 1ms 到达,只要它的 .then 已注册,就大概率比同轮的 XHR.onload 更早执行
为什么会有这个差异?根源在 Web API 层
事件循环本身不决定“谁进哪个队列”,而是由浏览器的 Web API 模块在响应就绪时主动选择:
立即学习“Java免费学习笔记(深入)”;
- XHR 是较老的 API,设计上沿用事件驱动模型,浏览器将其回调统一按宏任务调度
- Fetch 是 Promise-first 的现代 API,浏览器在 resolve Promise 时,自动触发 Promise 链的微任务入队机制
- 注意:fetch() 本身不产生微任务;是 response.json().then() 这类操作触发了微任务——因为 Promise 方法内部调用 queueMicrotask
实际影响:别依赖执行顺序做逻辑耦合
由于宏任务和微任务的调度优先级不同,混合使用 XHR 和 Fetch 时,回调执行顺序可能反直觉:
- 同步代码 → 微任务(fetch.then)→ 宏任务(XHR.onload)→ 下一轮微任务
- 哪怕 XHR 先收到响应,它的 onload 也可能比后发出的 fetch.then 更晚执行
- 若业务逻辑强依赖“谁先返回谁先处理”,应统一使用同一种请求方式,或显式用 Promise 包装 XHR


















