8.2 任务分类
事件循环的核心是任务调度,浏览器将异步任务按优先级划分为**宏任务(MacroTask / Task)与微任务(MicroTask / Job)**两大类,分别维护独立的任务队列。两类任务遵循不同的调度规则,微任务具备更高的执行优先级,能够在两次宏任务之间插队执行,以此实现高优先级任务的及时响应,是理解异步代码执行顺序的核心依据。
8.2.1 宏任务与微任务的区分
两类任务的核心差异在于调度层级与执行优先级,分别对应不同的业务场景与时间粒度。
一、核心定位差异
- 宏任务:浏览器级别的任务,由浏览器的各个独立线程触发,对应完整的、独立的异步工作单元。每一个宏任务都是一次完整的任务调度单元,事件循环每轮只会处理一个宏任务。
- 微任务:JavaScript 引擎级别的任务,是当前任务执行过程中产生的小型插队任务。微任务不进入独立的任务调度队列,而是在当前宏任务执行结束后、下一个宏任务开始前批量执行。
二、核心维度对比
对比维度 | 宏任务(MacroTask) | 微任务(MicroTask) |
调度层级 | 浏览器层面调度 | JS 引擎层面调度 |
队列特性 | 一个事件循环可存在多个宏任务队列(定时器队列、事件队列等) | 仅有一个全局微任务队列 |
执行规则 | 每轮事件循环仅执行一个宏任务 | 当前宏任务结束后,全部清空微任务队列 |
执行粒度 | 粗粒度,独立完整的任务单元 | 细粒度,小型补充任务 |
产生方式 | 浏览器 Web API 触发回调产生 | JS 执行过程中主动生成 |
响应延迟 | 较高,需要等待轮询调度 | 极低,当前任务结束立即执行 |
8.2.2 常见宏任务与微任务清单
一、常见宏任务
所有由浏览器 Web API 触发、具备独立回调的异步任务均属于宏任务:
任务类型 | 说明 |
整体 | 页面加载后,整段同步脚本本身是第一个宏任务,由浏览器主线程调度执行 |
| 定时器回调,由定时器线程计时结束后推入宏任务队列 |
DOM事件回调 | 点击、滚动、键盘输入等用户交互事件,由事件线程触发后推入宏任务队列 |
网络请求回调 |
|
文件 I/O回调 |
|
| IE 浏览器专属的立即执行宏任务,优先级高于普通定时器 |
UI渲染更新 | 浏览器的重排重绘、页面渲染本身也是宏任务级别的操作 |
二、常见微任务
所有在 JS 执行上下文内产生、需要插队执行的异步任务均属于微任务:
任务类型 | 说明 |
| Promise 的状态变更回调,是最典型的微任务 |
| 异步函数的等待回调,底层基于 Promise 实现,本质是微任务 |
| 原生手动创建微任务的 API,用于显式插队执行 |
| DOM 变化监听回调,DOM 批量变化后触发微任务 |
| 已废弃的对象变化监听,属于微任务 |
三、特殊任务说明
requestAnimationFrame:渲染前回调,在微任务清空后、正式渲染前执行,属于宏任务与渲染之间的特殊阶段,优先级高于普通宏任务。requestIdleCallback:浏览器空闲时执行的低优先级任务,在所有宏任务、微任务、渲染都完成后,页面空闲时才执行,优先级最低。
8.2.3 微任务优先执行机制
微任务优先是事件循环的核心调度规则:一个宏任务执行完成后,不会立即执行下一个宏任务,而是先清空当前所有的微任务队列,微任务全部执行完毕后,再进入下一轮宏任务调度。
一、完整事件循环执行流程
- 从宏任务队列中取出优先级最高的一个任务,压入调用栈开始执行。
- 执行过程中遇到微任务,将其加入微任务队列。
- 当前宏任务执行完毕,调用栈完全清空。
- 清空微任务队列:依次取出所有微任务执行,执行过程中新产生的微任务也继续加入队列,直到整个微任务队列完全清空。
- 浏览器执行 UI 渲染更新(可选,根据渲染时机与页面状态决定)。
- 回到步骤 1,执行下一个宏任务,开启新一轮循环。
核心特点:微任务具备「插队权」,只要微任务队列不为空,就不会执行下一个宏任务;即使微任务内部再创建微任务,也会在当前轮次全部执行完毕。
二、经典执行顺序示例
console.log('1. 同步代码开始'); // 宏任务:定时器 setTimeout(() => { console.log('2. setTimeout 宏任务'); }, 0); // 微任务:Promise Promise.resolve().then(() => { console.log('3. Promise then 微任务1'); // 微任务中再产生微任务 Promise.resolve().then(() => { console.log('4. Promise then 微任务2'); }); }); console.log('5. 同步代码结束');执行结果
1.同步代码开始
5.同步代码结束
3. Promise then微任务1
4. Promise then微任务2
2. setTimeout宏任务
执行顺序解析
- 执行整体 script 宏任务,先运行所有同步代码,依次输出 1、5。
- 同步代码执行过程中,定时器回调加入宏任务队列,Promise then 加入微任务队列。
- 当前宏任务(script)执行完毕,开始清空微任务队列。
- 执行第一个微任务,输出 3;执行过程中产生新的微任务,加入微任务队列末尾。
- 继续清空微任务,执行第二个微任务,输出 4,微任务队列完全清空。
- 微任务全部执行完毕,进入下一轮宏任务调度,执行定时器回调,输出 2。
三、核心应用意义
- 高优先级任务插队:需要立即响应的逻辑可以包装为微任务,确保在当前任务结束后立刻执行,避免等待多个宏任务调度。
- 避免阻塞渲染:耗时计算拆分为多个微任务分批执行,避免长时间阻塞宏任务导致页面卡顿。
- 状态同步:利用微任务优先于渲染的特性,在渲染前同步最终