Skip to content

Latest commit

 

History

History
101 lines (79 loc) · 6.04 KB

JavaScript 运行机制 — 事件循环(Event-Loop).md

File metadata and controls

101 lines (79 loc) · 6.04 KB

JavaScript 运行机制 — 事件循环(Event-Loop)

JavaScript 有一个基于事件循环的并发模型,事件循环负责执行代码、收集和处理事件以及执行队列中的子任务。

大家都知道 JS 是一门单线程的非阻塞的脚本语言。这也就意味着,代码在执行的任何时候只有一个主线程来处理所有的任务。所以弄懂事件循环机制对我们学习 JS 至关重要。

JS 单线程基于事件循环:分为异步任务和同步任务。同步任务执行完,在执行异步任务中的内容。

基本的执行步骤如下:

  • 所有的同步任务都在主线程上执行,形成一个执行栈(execution context stack)
  • 主线程之外,还存在一个任务队列(task queue)。只要异步任务有了运行结果,就在任务队列中放置一个事件。
  • 一旦执行栈中的所有同步任务执行完毕,系统会到任务队列读取并执行已经运行完且正在等待进入执行栈的任务。
  • 上述过程会不断重复,也就是常说的 Event Loop(事件循环)。

其中,任务队列又分为宏任务队列微任务队列

  • 当我们执行一个脚本时,它会自上而下运行
  • 遇到同步代码按顺序执行,遇到宏任务放入宏任务队列,遇到微任务放入微任务队列
  • 执行完当前宏任务,检查微任务队列,执行并清空微任务队列,如果在微任务的执行中又加入了新的微任务,也会在这一步一起执行
  • 从任务队列取出下一个宏任务并执行

Tips:其实完整的事件循环要复杂的多。另外,在每个微任务队列执行完之后,下一个宏任务开始之前,还存在一个渲染阶段。关于这些将不在本文进行介绍。您可以在最下面提供的文章找到答案。

最后,来看一下都有哪些宏任务和微任务队列:

  • 宏任务(Macro Task)— 整体脚本代码、UI 渲染、I/O 操作、postMessageMessageChannelrequestAnimationFramesetTimeoutsetIntervalsetImmediate(Node.js 环境)
  • 微任务(Micro Task)— Promise 的回调函数(then()catch()finally())、process.nextTick(Node.js 环境)、MutationObserver

示例

下面是一段面试常考的题目:

// 以下代码在 Node 环境运行:process.nextTick 由 Node 提供
console.log('1')
setTimeout(function () {
  console.log('2')
  process.nextTick(function () {
    console.log('3')
  })
  new Promise(function (resolve) {
    console.log('4')
    resolve()
  }).then(function () {
    console.log('5')
  })
})

process.nextTick(function () {
  console.log('6')
})

new Promise(function (resolve) {
  console.log('7')
  resolve()
}).then(function () {
  console.log('8')
  Promise.resolve().then(() => console.log(123213))
})

setTimeout(function () {
  console.log('9')
  process.nextTick(function () {
    console.log('10')
  })
  new Promise(function (resolve) {
    console.log('11')
    resolve()
  }).then(function () {
    console.log('12')
  })
})

// 最终输出:1 7 6 8 2 4 3 5 9 11 10 12

这里需要额外注意两点:

  • Node 的 process.nextTick 有专门的 nextTick 队列运行,它总是在其他微任务之前运行。
  • 在 Node.js 中运行上述代码,如果版本在 v11 以上,那么不会有什么问题,但如果在这个版本以下的话,最终输出的结果会不一致。原因在于,浏览器的 Event Loop 和 Node.js 的 Event Loop 在处理异步事件的顺序是不同的。Node.js 的事件循环会先执行所有的宏任务,再执行微任务。

进一步阅读

这里看了很多资料,都整理了过来,感兴趣的可以了解以下: