事件循环 Microtasks (微任务) 的运行时机——从接受知识到探索v8源码

本文通过一个有趣的例子探讨JavaScript中的微任务Microtasks的运行时机,揭示了当V8执行完调用返回Blink时,由于MicrotasksScope作用域失效,会在其析构函数中检查JS调用栈是否为空,如果为空则执行Microtasks。文章详细介绍了手动点击和JS触发点击事件时Microtasks的执行顺序差异,并推荐使用Chrome Devtool的Performance和tracing工具进行浏览器运行原理的探索。
摘要由CSDN通过智能技术生成

Microtasks(微任务)是事件循环中一类优先级比较高的任务,本文通过一个有趣的例子探索其运行时机。从两年前被动接受知识 "当浏览器JS引擎调用栈弹空的时候,才会执行 Microtasks 队列",到两年后主动深入探索源码后了解到的 "当 V8 执行完调用要返回 Blink 时,由于 MicrotasksScope 作用域失效,在其析构函数中检查 JS 调用栈是否为空,如果为空就会运行 Microtasks。"。同时文章中介绍了用于探索浏览器运行原理的一些工具。

一个有趣的例子

刚学前端那会学习事件循环,说事件循环存在的意义是由于 JavaScript 是单线程的,所以需要事件循环来防止JS阻塞,让网络请求等I/O操作不阻塞主线程。

而 Microtasks 是一类优先级比较高的任务,我们不能像 Macrotasks(宏任务) 一样插入 Macrotasks 队列末端,等待多个事件循环后才执行,而需要插入到 Microtasks 的队列里面,在本轮事件循环中执行。

比如下面这个有趣的例子:

document.body.innerHTML = ` 
    <button id="btn" type="button">btn</button> 
`; 

const button = document.getElementById('btn')
 
button.addEventListener('click',()=>{
  Promise.resolve().then(()=>console.log('promise resolved 1'))
  console.log('listener 1')
})
 
button.addEventListener('click',()=>{
  Promise.resolve().then(()=>console.log('promise resolved 2'))
  console.log('listener 2')
})
 
// 1. 手动点击按钮 
// button.click() // 2. 解开这句注释,用JS触发点击行为

当我手动点击按钮的时候,大家觉得浏览器的输出是下面的A还是B?

  • A. listener1 -> promise resolved 1 -> listener2 -> promise resolved 2
  • B. listener1 -> listener2 -> promise resolved 1 -> promise resolved 2

大家可以在这里试一下:

https://codesandbox.io/static/img/play-codesandbox.svg

当我将上面代码中的最后一行注释打开,使用JS触发点击行为的时候,浏览器的输出是A还是B?

大家觉得上面1、2两种情况的输出顺序是否一样?

答案非常有意思

  • 当我们使用1. 手动点击按钮时,浏览器的输出是A
  • 当我们使用2. 用JS触发点击行为时,浏览器的输出是B

被动接受知识

为什么会出现这种情况呢? 这个 Microtasks 的运行时机有关。两年前当我带着这个问题搜索资料并询问大佬的时,大佬告诉我:

当浏览器JS引擎调用栈弹空的时候,才会执行Microtasks队列

按照这个结论,我使用 Chrome Devtool 中的 Performance 做了一次探索

人工点击按钮

人工点击的时候输出为 listener1 -> promise resolved 1 -> listener2 -> promise resolved 2 。

  • 从上图中我们可以看到,一次点击事件之后,浏览器会调用 Function Call 进入JS引擎,执行 listener1,输出listener1
  • 弹栈时发现JS调用栈为空,这时候就会执行 Microtasks 队列中的所有 Microtask,输出promise resolved 1
  • 接着浏览器调用 Function Call 进入JS引擎,执行 listener2,输出listener 2
  • 弹栈时发现JS调用栈为空,这时候就会执行 Microtasks 队列中的所有Microtask,输出promise resolved 2

JS触发点击事件

在JS代码中触发点击时输出为 listener1 -> listener2 -> promise resolved 1 -> promise resolved

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值