不求谌解

不求谌解

💻 Web Dev / Creative 💗 ⚽ 🎧 🏓
twitter
github
jike
email

再探js执行机制

写在前面#

入行前端已两年有余了。之前我在一篇文章中写了 —— 要明白前端领域的变与不变。现在和两年前一样,各种框架层出不穷,让人眼花缭乱。很多时候我也在问自己『这个还需要学吗』。这样很容易让我产生焦虑感。而且在面对新事物时,我经常会感到手足无措。归根结底还是基础知识不扎实,内功不够。这也是我写这篇文章的目的 —— 提升内功,更好的理解『变与不变』。

要开始了#

先从这段代码开始探索之旅吧

console.log('开始了')

// setTimeout1
setTimeout(function () {
  console.log('timeout1')
  // promise1
  new Promise(function (resolve) {
    console.log('timeout1_promise')
    resolve()
  }).then(function () {
    console.log('timeout1_then')
  })
}, 2000)

for (var i = 1; i <= 5; i++) {
  // setTimeout2
  setTimeout(function () {
    console.log(i)
  }, i * 1000)
  console.log(i)
}

// promise2
new Promise(function (resolve) {
  console.log('promise1')
  resolve()
}).then(function () {
  console.log('then1')
})

// setTimeout3
setTimeout(function () {
  console.log('timeout2')
  // promise3
  new Promise(function (resolve) {
    console.log('timeout2_promise')
    resolve()
  }).then(function () {
    console.log('timeout2_then')
  })
}, 1000)

// promise4
new Promise(function (resolve) {
  console.log('promise2')
  resolve()
}).then(function () {
  console.log('then2')
})

开始之前,喜欢思考问题的小伙伴可能会有两个问题。javascript 为什么被设计为单线程语言,为什么又会有同步任务和异步任务的区分。这两个问题,在这里我就不细述了。感兴趣的同学请看阮老师的这篇文章
现在我们来看一下 js 大致是怎么执行的。

Event Loop

到底哪些是宏任务,哪些是微任务呢。大体上这样区分。

宏任务(macro-task)

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

微任务(micro-task)

  • promise
  • async/await (同☝)
  • process.nextTick
  • MutationObserver

预备知识已经准备的差不多了,现在我们开始执行上面那段代码。

  • 首先整体 script 进入主线程,遇到console.log(),立即输出『开始了』
  • 接下来遇到setTimeout,2s 后回调函数 function () 被分发到宏任务 Event Queue 中(注意这里不是 2s 后执行),这里标记为 setTimeout1
  • 遇到for,直接执行,同理setTimeout中的回调函数被分发到宏任务 Event Queue 中(这里涉及到闭包的知识),标记为 setTimeout2,然后执行console.log,输出『1,2,3,4,5』
  • 遇到promise,new Promise直接执行,输出『promise1』。then被分发到微任务 Event Queue 中,标记为 then1
  • 接下来又遇到了一个setTimeout,1s 后回调函数 function () 被分发到宏任务 Event Queue 中,标记为 setTimeout3
  • 又遇到promise,同理,输出『promise2』,then被分发到微任务 Event Queue 中,标记为 then2

第一轮事件循环的宏任务已经执行完毕。Event Queue 中的任务如下表所示

宏任务微任务
setTimeout1(2s later)then1
setTimeout2(1s later)then2
setTimeout3(1s later)

根据上面的流程图,宏任务执行完后,js 引擎的监视进程会检查此时有没有可以执行的微任务。这时后分发到微任务 Event Queue 的then将被执行。依次输出『then1』,『then2』

第一轮事件循环全部执行完毕。

好了,现在开始第二轮事件循环 (1s 后)。

  • 遇到setTimeout2, 输出『6』,没有可以执行的微任务。执行新的宏任务。
  • 遇到setTimeout3, 输出『timout2』,new promise立即执行,输出『timeout2_promise』,then被分发到微任务 Event Queue 中。标记为 then3
    第二轮事件循环的宏任务执行完毕。Event Queue 中的任务如下表所示
宏任务微任务
setTimeout1(1s later)then3
setTimeout2(1s later)

同理,宏任务执行完后。执行此轮的微任务 then3。

第二轮事件循环全部执行完毕。

  • 遇到setTimeout1,执行console.log,输出『timeout1』,new promise立即执行,输出『timeout1_promise』,then被分发到微任务 Event Queue 中。标记为 then4
  • 第三轮事件循环宏任务执行完毕,执行此轮的微任务then,输出『timeout1_then』

第三轮事件循环执行完毕。

  • setTimeout2中依次会产生 4 个宏任务,每隔 1s 输出一个 6

至此,整段代码全部执行结束。

总结:宏任务执行完了,执行该宏任务产生的微任务。如果微任务在执行过程中产生新的微任务,则继续执行微任务。微任务执行完毕后,回到宏任务中开始下一轮循环。

async#

node#

参考资料#

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。