queueMicrotask 是浏览器原生微任务API,将函数推入微任务队列末尾执行;相比 Promise.then,它更轻量、无Promise状态开销、不触发 unhandledrejection,语义更清晰。
queueMicrotask 是浏览器原生提供的微任务调度 API,作用是把一个函数推入当前事件循环的微任务队列末尾,在本轮宏任务结束后、下一轮宏任务开始前执行。它和 Promise.resolve().then(fn) 效果一致,但更轻量、语义更清晰——不创建 Promise 实例,没有状态管理开销,也不触发 Promise 的异常捕获链。
Promise.then 那样在抛错时触发未处理的 rejection(即不会触发 unhandledrejection 事件)Promise 构造函数的性能开销,尤其在高频调用场景(如渲染循环、输入节流)中更干净直接传入一个无参函数即可,queueMicrotask 不接受参数,需自行闭包捕获上下文:
let data = { count: 1 };queueMicrotask(() => { console.log('微任务执行', data.count); // ✅ 正确:闭包引用});<p>// ❌ 错误写法(会立即执行并返回结果)queueMicrotask(doSomething());</p><p>// ❌ 错误写法(this 绑定丢失,且无法传参)queueMicrotask(obj.method);</p>
如果需要传参或绑定 this,用箭头函数或 bind 包一层:
queueMicrotask(() => fn(a, b))queueMicrotask(fn.bind(obj, a, b))注意:不要在 queueMicrotask 回调里做耗时操作(如大量 DOM 遍历、JSON.parse 大字符串),它仍属于“本轮结束前必须完成”的微任务,阻塞后续微任务和 UI 渲染。
立即学习“前端免费学习笔记(深入)”;
适合的场景:
getBoundingClientRect()),避免强制同步布局(layout thrashing)setTimeout(..., 0) 更及时不适合的场景:
setTimeout 或事件监听queueMicrotask 不提供取消机制,得自己加标记位或改用 AbortController 配合其他方案typeof queueMicrotask === 'function' 再降级典型问题:插入节点后立即用 queueMicrotask 读取 offsetHeight,却还是 0。
原因往往不是 queueMicrotask 失效,而是:
document.createElement,但没 appendChild)display: none 或父容器不可见,导致尺寸计算为 0visibility: hidden(仍占布局空间,但 offsetHeight 正常;而 display: none 则为 0)验证方式:在 queueMicrotask 回调里加 console.log(element.offsetParent, getComputedStyle(element).display),确认元素已挂载且样式允许布局计算。
微任务本身很可靠,但它的“延后”是相对于 JS 执行栈而言的,不是相对于渲染帧——如果 DOM 变更触发了重排/重绘,浏览器可能还没完成样式计算,这时读尺寸仍不准。真正保险的做法是组合使用:queueMicrotask + requestAnimationFrame(后者保证在下一帧绘制前执行)。