0
点赞
收藏
分享

微信扫一扫

js 运行机制 Event loop


众所周知 JS 是门非阻塞单线程语言,因为在最初 JS 就是为了和浏览器交互而诞生的。如果 JS 是门多线程的语言话,我们在多个线程中处理 DOM 就可能会发生问题(一个线程中新加节点,另一个线程中删除节点),当然可以引入读写锁解决这个问题。

JS 在执行的过程中会产生执行环境,这些执行环境会被顺序的加入到执行栈中。如果遇到异步的代码,会被挂起并加入到 Task(有多种 task) 队列中。一旦执行栈为空,Event Loop 就会从 Task 队列中拿出需要执行的代码并放入执行栈中执行,所以本质上来说 JS 中的异步还是同步行为。

console.log('script start')

setTimeout(function() {
console.log('setTimeout')
}, 0)

console.log('script end')

// script start
// script end
// setTimeout

以上代码虽然 ​​setTimeout​​​ 延时为 0,其实还是异步。这是因为 HTML5 标准规定这个函数第二个参数不得小于 4 毫秒,不足会自动增加。所以 ​​setTimeout​​​ 还是会在 ​​script end​​ 之后打印。

不同的任务源会被分配到不同的 Task 队列中,任务源可以分为 微任务(microtask) 和 宏任务(macrotask)。在 ES6 规范中,microtask 称为 ​​jobs​​​,macrotask 称为 ​​task​​。

console.log('script start')

setTimeout(function() {
console.log('setTimeout')
}, 0)

new Promise(resolve => {
console.log('Promise')
resolve()
})
.then(function() {
console.log('promise1')
})
.then(function() {
console.log('promise2')
})

console.log('script end')

// script start => Promise => script end => promise1 => promise2 => setTimeout

以上代码虽然 ​​setTimeout​​​ 写在 ​​Promise​​​ 之前,但是因为 ​​Promise​​​ 属于微任务而 ​​setTimeout​​ 属于宏任务,所以会有以上的打印。

微任务包括 ​​process.nextTick​​​ ,​​promise​​​ ,​​Object.observe​​​ ,​​MutationObserver​

宏任务包括 ​​script​​​ , ​​setTimeout​​​ ,​​setInterval​​​ ,​​setImmediate​​​ ,​​I/O​​​ ,​​UI rendering​

很多人有个误区,认为微任务快于宏任务,其实是错误的。因为宏任务中包括了 ​​script​​ ,浏览器会先执行一个宏任务,接下来有异步代码的话就先执行微任务。

所以正确的一次 Event loop 顺序是这样的

  1. 执行同步代码,这属于宏任务
  2. 执行栈为空,查询是否有微任务需要执行
  3. 执行所有微任务
  4. 必要的话渲染 UI
  5. 然后开始下一轮 Event loop,执行宏任务中的异步代码

通过上述的 Event loop 顺序可知,如果宏任务中的异步代码有大量的计算并且需要操作 DOM 的话,为了更快的 界面响应,我们可以把操作 DOM 放入微任务中。

举报

相关推荐

0 条评论