参考

浏览器端
到底什么是事件循环 Event loop in JavaScript (b站搬运的油管视频)
Loupe—可视化查看event loop
https://mp.weixin.qq.com/s/m3a6vjp8-c9a2EYj0cDMmg

深入JavaScript中的EventLoop(b站另外一个搬运的油管视频)(包含异步api回调队列 和 u渲染 两部分动画演示)
对应油管地址,speaker is chrome develoer
https://www.youtube.com/watch?v=cCOL7MC4Pl0&t=1442s

事件循环是如何影响页面渲染的?(非常好博客 理解页面渲染以及 requestAnimationFrame,setTimeout 4ms延迟)
浏览器进程线程详解

Event Loop 这个循环你晓得么?(附GIF详解)(饿了么前端)(附node event loop)

包含浏览器端 和 node端
详解JavaScript中的Event Loop(事件循环)机制 (知乎专栏)
通杀Event Loop面试题! (b站 培训机构讲的 非常好)

task 介绍
Tasks, microtasks, queues and schedules (博客 深入JavaScript中的EventLoop 同一个大佬,这边博客又提到了到底什么是事件循环 Event loop in JavaScript )

0 前提

image.png

1. 名词

1.1 微任务 MicroTask

ES原生 Promise

1.2 宏任务 MacroTask

浏览器端: 定时器(setTimeout 和 setInterval)
ajax
UI 渲染
页面交互
DOM事件 requestAnimationFrame

node端: 定时器(setTimeout 和 setInterval)
I/0

1.3 任务队列 task queue

1.4 node端事件循环独有的API

Process.nextTick
setImmediate

2. 浏览器端 event loop

2.1 任务队列分macro task queue和 micro task,一次事件循环,只会从宏任务队列中取出一个执行,然后清空所有微任务队列, 如此循环。

(下面图上 callback queue 没有区分macro 和 micro)
image.png

2.2 UI 渲染和交互的处理是通过 Task Queue 来调度的

image.png
因此耗时任务会导致渲染和交互任务得不到调用,也就是页面“卡死”。
如果设计一个耗时任务

循环 API 队列类型 期间页面能否交互? 每秒执行次数
while(true) 当前任务 否 701665.8
Promise Microtask Queue 否 609555.4
setTimeout Task Queue 是 208.3
requestAnimationFrame Task Queue 是 59
  1. 单个的耗时任务和 Microtask Queue 都会阻塞页面交互

(因为JavaScript 有 “run-to-completion” 的特性,以及在执行下个task是,要清空当前micro task)

  1. Macro Task 则不影响。 因为 Macro Task 之间浏览器有机会会插入 UI 任务。

扩展:一下代码页面背景是否会闪现一下红色?直觉总会有这样的担心

  1. document.body.style.background = 'red';
  2. document.body.style.background = 'white';

答案:
背景会稳定地呈现白色。 因为 JavaScript “run-to-completion” 的特性,在上述两行代码之间不可能插入渲染任务

3. node端 event loop

image.png
image.png

3.1 node端事件循环不同于浏览器端,一次循环包含六个阶段,主要关注一下三个阶段

  1. timers (定时器执行阶段)
  2. poll (I/0 callback执行阶段,大部分异步api回调在这个阶段执行,)

(不同于 I/0 callbacks 阶段,目前这个部分和poll阶段 从网上看到的资料讲解的不太统一,还没搞明白,暂时把i/o回调放在这里)

  1. check ( setImmediate 执行阶段)

注意: process.nextTick() 不存在以上任何一个阶段,而是介于每个阶段的中间。

事实上,每个阶段都包含三个队列(这里把process.nextTick包含到以上提到的阶段里面了,便于理解)
其中process.nextTick优先级高于promise
image.png

3.2 不确定定性

  1. setTimeout(function() {console.log("setTimeout")}, 0)
  2. setImmediate(() => {
  3. console.log('setImmediate');
  4. });

以上代码 那个先执行?
答案: 随机,都有可能。
因为: image.png