Js 主线程和异步队列哪个先执行

发布于:2025-04-03 ⋅ 阅读:(31) ⋅ 点赞:(0)

JavaScript 主线程与异步队列执行顺序详解

JavaScript 是单线程语言,通过事件循环(Event Loop)机制来处理同步和异步任务。以下是主线程与异步队列的执行顺序解析:

1. 执行顺序基本原则

console.log('1. 主线程同步任务');

setTimeout(() => {
  console.log('6. 宏任务队列 - setTimeout');
}, 0);

Promise.resolve().then(() => {
  console.log('4. 微任务队列 - Promise');
});

console.log('2. 主线程同步任务');

queueMicrotask(() => {
  console.log('5. 微任务队列 - queueMicrotask');
});

console.log('3. 主线程同步任务');

// 输出顺序:
// 1. 主线程同步任务
// 2. 主线程同步任务
// 3. 主线程同步任务
// 4. 微任务队列 - Promise
// 5. 微任务队列 - queueMicrotask
// 6. 宏任务队列 - setTimeout

2. 执行顺序详细机制

执行栈(Call Stack)

  • 同步代码立即执行
  • 函数调用形成栈帧

任务队列(Task Queue)

  1. 微任务队列(Microtask Queue)

    • Promise 回调(then/catch/finally)
    • queueMicrotask()
    • MutationObserver(浏览器)
    • process.nextTick(Node.js)
  2. 宏任务队列(Macrotask Queue)

    • setTimeout/setInterval
    • I/O 操作
    • UI 渲染(浏览器)
    • setImmediate(Node.js)
    • requestAnimationFrame(浏览器)

3. 完整事件循环流程

  1. 执行当前同步代码(主线程)
  2. 执行完所有同步代码后,检查微任务队列
    • 执行所有微任务(直到队列为空)
  3. 如有需要,进行UI渲染(浏览器环境)
  4. 宏任务队列取出一个任务执行
  5. 重复上述过程(事件循环)

4. 复杂示例分析

console.log('脚本开始');

setTimeout(() => {
  console.log('setTimeout1');
  Promise.resolve().then(() => {
    console.log('Promise in setTimeout1');
  });
}, 0);

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

Promise.resolve().then(() => {
  console.log('Promise1');
  Promise.resolve().then(() => {
    console.log('Promise in Promise1');
  });
});

Promise.resolve().then(() => {
  console.log('Promise2');
});

console.log('脚本结束');

/* 输出顺序:
   脚本开始
   脚本结束
   Promise1
   Promise2
   Promise in Promise1
   setTimeout1
   Promise in setTimeout1
   setTimeout2
*/

5. Node.js 与浏览器的差异

环境 微任务优先级 宏任务优先级顺序
浏览器 Promise > MutationObserver 动画帧 > I/O > 定时器 > UI渲染
Node.js process.nextTick > Promise setImmediate > setTimeout/setInterval

6. 实践建议

  1. 长时间运行的微任务会阻塞渲染和宏任务执行

    // 避免这种情况
    Promise.resolve().then(() => {
      while(true) { /* 无限循环 */ }
    });
    
  2. 合理分配任务类型

    • 高优先级任务:使用微任务
    • 低优先级任务:使用宏任务
  3. 避免嵌套太深

    // 不易维护的深层嵌套
    setTimeout(() => {
      Promise.resolve().then(() => {
        setTimeout(() => {
          // ...
        }, 0);
      });
    }, 0);
    

理解这些执行顺序规则对于调试异步代码、优化性能和避免竞态条件至关重要。


网站公告

今日签到

点亮在社区的每一天
去签到