应根据任务特性选择:紧急且需即时ui响应用微任务,对齐渲染节奏用requestanimationframe,非关键任务用宏任务。微任务如promise.then适合状态更新;raf用于动画;settimeout或messagechannel处理延迟任务。

开发中选宏任务还是微任务,关键看任务的紧急程度、是否影响用户感知、以及是否需要等待浏览器渲染完成。不是越快越好,而是该快时快、该等时等。
需要立刻响应的状态更新,用微任务
比如表单校验失败后立即显示错误提示、Promise链中连续更新UI状态、或者确保多个响应式更新合并为一次DOM刷新(类似 Vue 的 nextTick 默认行为)。微任务在当前同步代码结束后马上执行,不等渲染,视觉上更“即时”。
- 用 Promise.resolve().then() 或 queueMicrotask() 包裹 UI 状态变更
- 避免在微任务里反复触发新微任务(如递归调用 queueMicrotask),否则可能阻塞后续宏任务
- 注意:微任务不会等页面重绘,所以不适合用于“动画下一帧”这类依赖渲染节奏的操作
要对齐浏览器渲染节奏,选宏任务中的 requestAnimationFrame
如果你的操作直接影响视觉效果——比如动画位移、滚动位置修正、Canvas 绘制——那就该用 requestAnimationFrame。它不是普通宏任务,而是被浏览器专门调度在下一次重绘前执行,能保证与屏幕刷新率同步,不掉帧。
- 代替 setTimeout(0) 做动画更新,例如:requestAnimationFrame(() => { updatePosition(); })
- 多个 RAF 调用会自动合并到同一帧,比手动节流更可靠
- 它比 Promise.then 更晚执行,但比 setTimeout(0) 更准时、更高效
非关键或可延迟的任务,扔进 setTimeout 或 postMessage
日志上报、埋点统计、预加载低优先级资源、清理缓存等,不需要用户立刻看到结果,也不影响主线程响应。这类任务放宏任务里,能让出执行权,避免挤压用户交互或渲染任务。
- 用 setTimeout(fn, 0) 是最通用的降级方案,所有环境都支持
- 现代浏览器可用 MessageChannel 实现更轻量的宏任务(比 setTimeout 开销小)
- 避免在高频事件(如 scroll、input)里直接触发宏任务,应节流或防抖后再发
批量 DOM 操作或规避强制同步布局,靠组合策略
单次操作多个节点、或读写交替频繁时,容易触发重排重绘。这时可借助微任务“攒批”,或用宏任务“错峰”。比如:
- 先用 document.querySelectorAll 读取所有目标元素(同步)
- 再用 queueMicrotask 统一修改 class / style,让浏览器一次性处理
- 若涉及尺寸测量(offsetHeight 等),必须等渲染后才能读,就得用 requestAnimationFrame + setTimeout 组合:RAF 后触发 setTimeout(0),确保跨帧读取











