Live Note

Remain optimistic

Event Loop

浏览器中的事件循环

JavaScript 代码的执行过程中,除了依靠函数调用栈来搞定函数的执行顺序外,还依靠任务队列(task queue)来搞定另外一些代码的执行。整个执行过程,我们称为事件循环过程。一个线程中,事件循环是唯一的,但是任务队列可以拥有多个。任务队列又分为 macro-task(宏任务)与 micro-task(微任务),在最新标准中,它们被分别称为 task 与 jobs。

macro-task 大概包括:

  • script(整体代码)
  • setTimeout
  • setInterval
  • setImmediate
  • I/O
  • UI render

micro-task 大概包括:

  • process.nextTick
  • Promise
  • async/await(还是 Promise)
  • MutationObserver(HTML5)

整体流程大概为:

执行 macro-task,然后执行该任务产生的 micro-task,若 micro-task 在执行过程中新生成了 micro-task,则继续执行 micro-task。直到无 micro-task,返回 macro-task,进行 event loop。

答案为:

  1. async2 end
  2. Promise
  3. async1 end
  4. promise1
  5. promise2
  6. setTimeout

async/await 执行顺序

我们知道 async 隐式返回 Promise 函数,可以理解为 await 后的语句执行结束后,await 产生一个 micro-task。但是时机在与执行完 await 后的语句之后,会暂停当前执行,转而执行其他 loop。然后按照 micro-task 的注册顺序,继续执行。最后进入下一个 macro-task。

Node 和 Browser 的 Event Loop 差异

  • Browser 中的 micro-task 是在每个相应的 macro-task 中执行的。
  • NodeJS 中的 micro-task 是在不同阶段中执行的。

NodeJS 版本差异

NodeJS11 之后,会在执行一个 macro-task 之后立即执行他的 micro-task 队列。

1
2
3
4
5
6
7
8
9
10
11
12
setTimeout(() => {
console.log("timer1")
Promise.resolve().then(function () {
console.log("promise1")
})
}, 0)
setTimeout(() => {
console.log("timer2")
Promise.resolve().then(function () {
console.log("promise2")
})
}, 0)
  • node11 之前:timer1 -> timer2 -> promise1 -> promise2
  • node11 之后:timer1 -> promise1 -> timer2 -> promise2