从浏览器中的Event Loop(事件循环)机制探索JavaScript异步

Event Loop不同的实现

事件(event):事件就是由于某种外在或内在的信息状态发生的变化,从而导致出现了对应的反应。
比如说用户点击了一个按钮,就是一个事件;HTML页面完成加载,也是一个事件。
而且一个事件中会包含多个任务。

①浏览器在HTML Standard中定义了Event Loop
②Node.js使用了libuv库来实现Event Loop(本人完全不懂Node,用的是Python)

浏览器的Event Loop和Node.js的Event Loop是两个概念,本文只描述基于浏览器的Event Loop

JavaScript中,任务被分为Task(又称为MacroTask,宏任务)和MicroTask(微任务)两种

MacroTask:

MacroTask任务源非常宽泛,总结来说task任务源:
①setTimeout
②setInterval
③I/O
④UI rendering
⑤script
⑥setImmediate(Node.js API)

每一个event loop有一个或者多个task队列。
当用户代理安排一个任务,必须将该任务增加到相应的event loop的一个tsak队列中。
比如可以为鼠标、键盘事件提供一个task队列,其他事件又是一个单独的队列。

注:task也被称为MacroTask,由指定的任务源去提供任务

MicroTask:

HTML Standard没有具体指明哪些是MicroTask任务源,通常认为是MicroTask任务源有:
①promises
②MutationObserver MutationObserver API
③process.nextTick(Node.js API)
④Object.observe(已废弃)

在Promises / A +规范的Notes 3.1中提及了promise的then方法可以采用“宏任务”机制或者‘微任务’机制来实现。所以在不同浏览器的差异正源于此,有的浏览器将then放入了宏任务队列,有的放入了微任务队列
每一个event loop有一个且只有一个microtask队列

注:microtask 队列和task 队列有些相似,由指定的任务源去提供任务;

Event Loop的处理过程(Processing model)

在HTML Standard中Processing model 定义了event loop的循环过程:

一个event loop只要存在,就会不断执行下边的步骤:
1.在tasks队列中选择最先进入的task(!!!参考最下面的思考题),用户代理可以选择任何task队列,如果没有要选择的任务,则跳到下边的microtasks步骤。
2.将上边选择的task设置为currently running task。
3.Run: 运行被选择的task。
4.将event loop的currently running task设置回null。
5.从task队列中移除已经完成运行的task。
6.Microtasks: 执行microtasks checkpoint。(也就是执行microtasks队列里的任务 )
7.更新渲染(Update the rendering)...
8.返回到第一步。

event loop会不断循环上面的步骤,概括说来:
1.event loop会不断循环的去取tasks队列中最老的一个任务推入栈中执行,并在当次循环里依次执行并清空microtask队列里的任务。
2.执行完microtask队列里的任务,有可能会渲染更新。(浏览器很聪明,在一帧以内的多次dom变动浏览器不会立即响应,而是会积攒变动以最高60HZ的频率更新视图)

执行进入microtask检查点时,用户代理会执行以下步骤:

1.将microtask checkpoint的flag设为true。
2.Microtask queue handling: 如果event loop的microtask队列为空,直接跳到第8步(Done)。
3.在microtask队列中选择最先进入队列的一个任务。
4.将上边选择的microtask设为event loop的currently running task。
5.运行被选择的任务。
6.将event loop的currently running task设置回null。
7.从microtask队列中移除已经完成运行的microtask,然后返回到第2步(Microtask queue handling)。
8.Done: 每一个environment settings object它们的 responsible event loop就是当前的event loop,会给environment settings object发一个 rejected promises 的通知。
9.清理IndexedDB的事务。
10.将microtask checkpoint的flag设为flase。

microtask checkpoint所做的就是执行microtask队列里的任务。什么时候会调用microtask checkpoint呢?
1.当上下文执行栈为空时,执行一个microtask checkpoint。
2.在event loop的第六步(Microtasks: Perform a microtask checkpoint)执行checkpoint,也就是在运行task之后,更新渲染之前。

示例

在这里插入图片描述

HTML Standard 8.1.4 Event loops

An event loop has one or more task queues. A task queue is an ordered list of tasks, which are algorithms that are responsible for such work as:
Parsing
The HTML parser tokenizing one or more bytes, and then processing any resulting tokens, is typically a task.
Callbacks
Calling a callback is often done by a dedicated task.
译:
事件循环具有一个或多个任务队列。任务队列是一个有序的任务列表,这些算法负责以下工作:
解析
HTML解析器标记一个或多个字节,然后处理任何生成的标记,是典型的一项任务(即我们解析和执行完<script>标签里的内容)
回调
调用回调通常由专门的任务完成。

过程:
script里的代码被列为一个task,放入task队列。
循环1
【task队列:script ;microtask队列:】
①从task队列中取出script任务,推入栈中执行。
②setTimeout列为task,第一个Promise.then列为microtask
【task队列:setTimeout;microtask队列:第一个Promise.then】
③执行microtask checkpoint,取出microtask队列的第一个Promise.then推入栈执行,将第二个promise.then列为microtask
【task队列:setTimeout;microtask队列:第二个Promise.then】
④取出microtask队列的第二个Promise.then推入栈执行
循环2
【task队列:setTimeout;microtask队列:】
⑤从task队列中取出setTimeout,推入栈中执行
⑥执行microtask checkpoint
【task队列: ;microtask队列:】

思考题(tasks队列先后进栈问题)
在这里插入图片描述

结语:Event Loop里面涉及很多东西,比如处理过程中的Update the rendering(更新渲染)等等,所以在下篇文章详细讲解了浏览器渲染机制的原理和过程

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值