javascript 依靠事件循环协调同步与异步任务,宏任务(如 settimeout、script)执行完后立即清空全部微任务(如 promise.then、queuemicrotask),微任务按 fifo 执行且不中断;渲染受帧率限制,requestanimationframe 属宏任务但优先级高;调试可借助 devtools performance 面板与 performance api。

JavaScript 是单线程语言,靠事件循环(Event Loop)协调同步任务与异步任务的执行顺序。它不是“多线程调度”,也不是“定时轮询”,而是一套严格按规范(如 HTML 和 ECMA-262)定义的协作机制。
宏任务与微任务是核心分层
事件循环每次只处理一个宏任务(macrotask),比如:script 脚本、setTimeout 回调、setInterval 回调、I/O 事件、UI 渲染等。宏任务执行完后,会清空当前所有微任务(microtask)队列,比如 Promise.then/catch/finally、queueMicrotask、MutationObserver 回调。
- 微任务总在当前宏任务结束、下一个宏任务开始前执行,且不中断——全部跑完才让出控制权
- Promise 构造函数内的代码是同步执行的,只有 then/catch 进入微任务队列
- 多个 queueMicrotask 或 Promise.then 会按加入顺序依次执行,不会被新加入的微任务插队(FIFO)
渲染时机受事件循环约束
浏览器通常在一次宏任务结束后检查是否需要渲染。但渲染不是每次宏任务后都发生——它受帧率限制(如 60fps ≈ 每 16ms 一帧)和浏览器优化策略影响。例如:
- 连续快速触发的 resize 或 scroll 事件可能被节流,合并进同一个宏任务或延迟到空闲时处理
- 若某个宏任务运行太久(如长循环),会阻塞渲染和用户交互,页面“卡死”
- requestAnimationFrame 回调属于宏任务,但浏览器保证它在下一帧渲染前执行,优先级高于普通 setTimeout
不同 API 的任务类型需记牢
写异步逻辑时,搞错任务类型会导致意料外的执行顺序。常见归类如下:
- 宏任务:setTimeout、setInterval、setImmediate(Node.js)、I/O 回调、用户交互事件(click、input)
- 微任务:Promise.then/catch/finally、queueMicrotask、MutationObserver 回调、Object.observe(已废弃)
- 特殊行为:requestAnimationFrame 属于宏任务,但有独立调度;fetch().then 中的回调是微任务(因为基于 Promise)
实际调试可借助 DevTools
Chrome DevTools 的 Performance 面板能录制并可视化事件循环各阶段:Parse HTML、Function Call、Rendering、Painting、GC 等。配合 console.time/timeEnd 或 performance.now(),能定位长任务(Long Task > 50ms)。
- 在 Sources 面板打断点后,Call Stack 显示当前执行上下文,Tasks 列表显示待处理的宏/微任务
- 使用 Performance API 的 getEntriesByType('measure') 可记录微任务耗时(需手动打点)
- 避免在微任务中递归调用 queueMicrotask,否则可能撑爆调用栈或饿死其他任务
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











