侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

事件循环与任务队列执行顺序分析

2025-12-11 / 0 评论 / 3 阅读

题目

事件循环与任务队列执行顺序分析

信息

  • 类型:问答
  • 难度:⭐⭐

考点

事件循环机制,宏任务与微任务,异步代码执行顺序

快速回答

当浏览器执行以下代码时:

console.log('script start');

setTimeout(() => console.log('setTimeout'), 0);

Promise.resolve()
  .then(() => console.log('promise1'))
  .then(() => console.log('promise2'));

console.log('script end');

输出顺序为:

  1. 'script start'
  2. 'script end'
  3. 'promise1'
  4. 'promise2'
  5. 'setTimeout'

核心原因:

  • 同步代码立即执行
  • 微任务(Promise)优先于宏任务(setTimeout)执行
  • 事件循环按阶段处理任务队列
## 解析

原理说明

浏览器事件循环(Event Loop)是协调同步/异步任务的核心机制:

  1. 调用栈(Call Stack):同步代码立即执行,形成执行栈
  2. 任务队列(Task Queues)
    • 微任务队列(Microtask Queue):Promise.then、MutationObserver、queueMicrotask
    • 宏任务队列(Macrotask Queue):setTimeout、setInterval、DOM事件、I/O
  3. 执行流程
    1. 执行同步代码(调用栈清空)
    2. 执行所有微任务(直到队列清空)
    3. 渲染页面(如需要)
    4. 执行一个宏任务
    5. 重复步骤2-4

代码示例分析

console.log('script start');  // 1. 同步任务

setTimeout(() => {
  console.log('setTimeout');  // 5. 宏任务
}, 0);

Promise.resolve()
  .then(() => {
    console.log('promise1');  // 3. 微任务
  })
  .then(() => {
    console.log('promise2');  // 4. 微任务(链式调用)
  });

console.log('script end');    // 2. 同步任务

执行步骤分解:

  1. 同步代码顺序执行,输出 'script start' 和 'script end'
  2. 调用栈清空后,检查微任务队列
  3. 执行 Promise.then 回调:输出 'promise1',并添加新微任务(第二个 then)
  4. 继续清空微任务队列:输出 'promise2'
  5. 执行下一个宏任务(setTimeout回调):输出 'setTimeout'

最佳实践

  • 性能优化:耗时操作用微任务拆分,避免阻塞渲染
    // 将长任务拆分为微任务队列
    function processChunk() {
      // ...处理数据块
      if (hasMore) queueMicrotask(processChunk);
    }
    queueMicrotask(processChunk);
  • 执行顺序控制:关键操作使用微任务确保优先执行
  • 避免嵌套过深:微任务队列连续执行可能导致阻塞

常见错误

  • 误判执行顺序:认为 setTimeout(0) 会立即执行
  • 微任务递归:微任务中递归添加微任务导致死循环
    // 错误示例:阻塞主线程
    function recursiveMicrotask() {
      Promise.resolve().then(recursiveMicrotask);
    }
    recursiveMicrotask();
  • 混用任务类型:未考虑 requestAnimationFrame 在渲染阶段的特殊性

扩展知识

  • Node.js vs 浏览器:Node.js 中 process.nextTick 优先级高于微任务
  • requestAnimationFrame:在渲染前执行,适合动画更新
  • 任务优先级
    1. 同步代码 > process.nextTick(Node) > 微任务 > 渲染 > 宏任务
  • 性能监测:使用 Performance API 分析任务耗时
    performance.mark('start');
    // 执行代码
    performance.mark('end');
    performance.measure('task', 'start', 'end');