宏任务(Macrotask)和微任务(Microtask)

84 阅读2分钟

宏任务(Macrotask)和微任务(Microtask)是JavaScript事件循环中的两个重要概念。它们决定了JavaScript代码的执行顺序和时机。理解宏任务和微任务对于深入理解JavaScript的异步编程非常重要。

宏任务(Macrotask)

宏任务是指在事件循环中的一次完整的执行机会。常见的宏任务包括:

  • 全局执行上下文(Global Execution Context)
  • 定时器(Timers) :如 setTimeout 和 setInterval
  • I/O 操作
  • UI 渲染
  • 事件处理程序(Event Handlers) :如点击事件、键盘事件等
  • Promise

微任务(Microtask)

微任务是指在当前宏任务执行完成后,但在控制权交给下一个宏任务之前执行的任务。常见的微任务包括:

  • Promise
  • MutationObserver
  • Process.nextTick(Node.js 中)
  • QueueMicrotask

事件循环(Event Loop)

事件循环是JavaScript引擎的核心机制之一,它决定了异步任务的执行顺序。事件循环的基本流程如下:

  1. 执行栈(Execution Context Stack) :存放当前正在执行的函数。
  2. 任务队列(Task Queue) :存放待执行的宏任务。
  3. 微任务队列(Microtask Queue) :存放待执行的微任务。

宏任务和微任务的执行顺序

  1. 执行当前宏任务

    • 当前宏任务执行完毕后,会清空当前微任务队列中的所有微任务。
    • 微任务会在当前宏任务执行完毕后立即执行,但在此之前不会执行其他宏任务。
  2. 执行微任务

    • 微任务队列中的所有任务都会依次执行。
    • 微任务执行完毕后,才会继续执行下一个宏任务。

示例代码

下面通过一个具体的示例来展示宏任务和微任务的执行顺序:

console.log('1');

setTimeout(() => {
  console.log('2');
  process.nextTick(() => {
    console.log('3');
  });
  Promise.resolve().then(() => {
    console.log('4');
  });
}, 0);

process.nextTick(() => {
  console.log('5');
});

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

queueMicrotask(() => {
  console.log('7');
});

console.log('8');

输出解析

  1. 全局执行上下文

    console.log('1');
    console.log('8');
    
  2. 第一个 process.nextTick

    process.nextTick(() => {
      console.log('5');
    });
    
  3. 第一个 Promise 的 then

    Promise.resolve().then(() => {
      console.log('6');
    });
    
  4. queueMicrotask

    queueMicrotask(() => {
      console.log('7');
    });
    
  5. setTimeout 内部的 process.nextTick 和 Promise 的 then

    setTimeout(() => {
      console.log('2');
      process.nextTick(() => {
        console.log('3');
      });
      Promise.resolve().then(() => {
        console.log('4');
      });
    }, 0);
    

最终输出顺序

  1. 1
  2. 8
  3. 5
  4. 6
  5. 7
  6. 2
  7. 3
  8. 4

详细解释

  1. 全局执行上下文

    • console.log('1'); 输出 1
    • console.log('8'); 输出 8
  2. 第一个 process.nextTick

    • process.nextTick(() => { console.log('5'); }); 输出 5
  3. 第一个 Promise 的 then

    • Promise.resolve().then(() => { console.log('6'); }); 输出 6
  4. queueMicrotask

    • queueMicrotask(() => { console.log('7'); }); 输出 7
  5. setTimeout 内部的 process.nextTick 和 Promise 的 then

    • setTimeout(() => { console.log('2'); ... }, 0); 输出 2
    • process.nextTick(() => { console.log('3'); }); 输出 3
    • Promise.resolve().then(() => { console.log('4'); }); 输出 4

总结

  • 宏任务:包括 setTimeoutsetInterval、事件处理程序等。
  • 微任务:包括 PromiseMutationObserverprocess.nextTick(Node.js)、queueMicrotask
  • 事件循环:先执行当前宏任务,然后清空当前微任务队列,再执行下一个宏任务。

通过理解宏任务和微任务的概念以及它们的执行顺序,可以更好地掌握JavaScript的异步编程机制。