一文搞懂js中的宏任务(Macro Task)和微任务(Micro Task)

248 阅读2分钟

在 JavaScript 中,任务调度机制分为宏任务(macro task)和微任务(micro task)。它们的执行顺序和调度方式有所不同。

宏任务(Macro Task)

宏任务是指那些在事件循环的每个循环迭代中执行的任务。常见的宏任务包括:

  • setTimeout
  • setInterval
  • setImmediate(Node.js 环境)
  • I/O 操作
  • UI 渲染

微任务(Micro Task)

微任务是在当前宏任务执行结束后立即执行的任务。常见的微任务包括:

  • Promise 的回调函数(thencatchfinally
  • MutationObserver
  • process.nextTick(Node.js 环境)

执行顺序

  1. 执行一个宏任务(从事件队列中取一个)。
  2. 执行过程中如果遇到微任务,就将它添加到微任务队列。
  3. 当前宏任务执行完毕后,立即执行所有微任务(依次执行微任务队列中的任务)。
  4. 执行完所有微任务后,再从事件队列中取下一个宏任务,进入下一个循环。

示例

以下是一个简单的示例,展示了宏任务和微任务的执行顺序:

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

解释

  1. 首先执行同步代码,输出 script start 和 script end
  2. setTimeout 是一个宏任务,它会在事件循环的下一个迭代中执行。
  3. Promise 的回调是微任务,会在当前宏任务执行完毕后立即执行。
  4. 因此,promise1 和 promise2 会在 setTimeout 之前执行。

结合代码示例

下面这段代码中,flushJob 函数使用了 Promise.resolve().then(...),这意味着 jobQueue 中的任务会作为微任务执行:

function flushJob() {
  if (isFlushing) return
  isFlushing = true
  p.then(() => { // 微任务
    jobQueue.forEach(job => job())
  }).finally(() => {
    isFlushing = false
  })
}

这确保了 jobQueue 中的任务会在当前宏任务执行完毕后立即执行,而不会等到下一个宏任务。