Skip to content

JavaScript 事件循环与任务队列

代码示例

javascript
const pro = new Promise((resolve, reject) => {
  const innerpro = new Promise((r, reject) => {
    setTimeout(() => {
      r(1);  // 宏任务,最后执行
    });
    console.log(2);  // 同步任务,立即执行
    r(3);  // 同步解析 innerpro
  });
  resolve(4);  // 同步解析 pro
  innerpro.then((res) => console.log(res));  // 微任务,排队等待
  console.log('yideng');  // 同步任务,立即执行
})
pro.then((res) => console.log(res));  // 微任务,排队等待
console.log('end');  // 同步任务,立即执行

执行顺序分析

1. 同步任务(立即执行)

console.log(2)      → 输出: 2
console.log('yideng') → 输出: yideng
console.log('end')   → 输出: end

2. 微任务(同步任务完成后执行)

innerpro.then((res) => console.log(res)) → 输出: 3
pro.then((res) => console.log(res))      → 输出: 4

3. 宏任务(微任务完成后执行)

setTimeout中的 r(1) → 无效(innerpro 已被解析为 3)

最终输出结果

2
yideng
end
3
4

核心知识点

Promise 解析规则

  • Promise 的 resolve/reject 调用是同步的
  • .then() 回调是异步的(微任务)
  • Promise 只能被解析一次,后续的解析调用会被忽略

事件循环机制

  1. 同步任务:在主线程上立即执行
  2. 微任务(Microtask):在当前宏任务结束后、下一个宏任务开始前执行
    • Promise.then/catch/finally
    • MutationObserver
    • queueMicrotask
  3. 宏任务(Macrotask):在微任务队列清空后执行
    • setTimeout/setInterval
    • setImmediate(Node.js)
    • I/O操作
    • UI渲染

执行顺序总结

同步代码 → 清空微任务队列 → 执行一个宏任务 → 清空微任务队列 → 执行下一个宏任务 → ...

关键要点

  1. innerpro 在同步代码中被 r(3) 解析,状态变为 fulfilled
  2. setTimeout 中的 r(1) 虽然执行了,但因为 Promise 已经是 fulfilled 状态,所以这次解析无效
  3. 微任务的执行顺序与注册顺序一致:先注册 innerpro.then,后注册 pro.then
  4. 所有微任务执行完毕后,才会执行宏任务队列中的任务