请解释事件循环中宏任务和微任务的执行顺序。

推荐答案

JavaScript 的事件循环中,宏任务(Macrotask)和微任务(Microtask)的执行顺序如下:

  1. 执行同步代码: 首先执行 JavaScript 代码中的同步部分,这些代码会立即执行。
  2. 执行宏任务: 当同步代码执行完毕后,会从宏任务队列中取出一个任务执行。常见的宏任务包括:
    • setTimeout
    • setInterval
    • setImmediate (Node.js 环境)
    • requestAnimationFrame
    • I/O 操作 (例如网络请求、文件读写)
    • 用户交互事件 (例如点击、滚动)
  3. 执行微任务: 当一个宏任务执行完毕后,会立即检查微任务队列,并执行队列中的所有微任务。常见的微任务包括:
    • Promise.thenPromise.catch
    • async/await(实际上是基于 Promise 实现的)
    • queueMicrotask (新的API)
    • MutationObserver
  4. 循环执行: 执行完所有微任务后,事件循环会再次从宏任务队列中取出一个任务执行,如此循环往复,直到宏任务队列为空。

总结: 执行顺序是:同步代码 -> 宏任务 -> 清空微任务队列 -> 宏任务 -> 清空微任务队列 ... 循环进行。

本题详细解读

宏任务与微任务的概念

  • 宏任务(Macrotask): 代表的是一些比较耗时的操作,例如 I/O,定时器等。它们会被放入宏任务队列中,每次事件循环只会执行一个宏任务。
  • 微任务(Microtask): 代表的是一些需要尽快执行的操作,例如 Promise 的回调,MutationObserver 的回调等。它们会被放入微任务队列中,每次宏任务执行完毕后,会清空微任务队列。

执行顺序详解

  1. 同步代码优先: JavaScript 引擎会先执行同步代码,这些代码直接在调用栈中执行。例如,变量声明、函数调用等。
  2. 宏任务队列与微任务队列: 当执行遇到异步操作(例如setTimeoutPromise.then),会将其回调函数放入对应的队列中。宏任务会放入宏任务队列,微任务会放入微任务队列。
  3. 事件循环的运作:
    • 事件循环会不断地检查调用栈是否为空。
    • 如果调用栈为空,则会检查宏任务队列是否为空。
    • 如果宏任务队列不为空,则会取出一个宏任务执行。
    • 执行宏任务后,会立即检查微任务队列是否为空。
    • 如果微任务队列不为空,则会依次执行所有微任务。
    • 清空微任务队列后,事件循环会再次回到第二步,继续检查宏任务队列。
    • 循环往复,直到宏任务队列为空。
  4. 关键点:
    • 一个宏任务执行完毕后,会立即清空微任务队列,才会执行下一个宏任务。 这保证了微任务的执行顺序优先于下一个宏任务。
    • 微任务的执行具有“插队”性质。 在当前宏任务的执行过程中,即使产生了新的宏任务,也会等到当前宏任务清空完微任务队列后,才会执行新的宏任务。

示例代码

-- -------------------- ---- -------
------------------- --------

--------------------- -
  --------------------------
-- ---

--------------------------------- -
  ------------------------
------------------ -
    ------------------------
---

------------------- ------

执行顺序分析:

  1. script start (同步代码)
  2. script end (同步代码)
  3. promise1 (微任务)
  4. promise2 (微任务)
  5. setTimeout (宏任务)

说明:

  • 同步代码 script startscript end 先执行。
  • Promise.then 产生的微任务 promise1promise2 在当前宏任务(同步代码)执行完后立即执行。
  • setTimeout 产生的宏任务 setTimeout 最后执行。

通过理解宏任务和微任务的执行顺序,可以更好地理解 JavaScript 的异步机制,并写出更高效、更可靠的代码。

纠错
反馈