又到了battle EventLoop的季节

金秋飒爽,丹桂飘香,我们又迎来了一年一度金九银十招聘季,有一个问题也是经常要cue到,逢人就要问:你知道EventLoop吗,展开说说?

简介

Event Loop ,“事件循环”

浏览器 or Node的一种解决JavaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。

为什么要弄懂Event Loop?

  • 增加技术深度,懂得JS运行机制
  • 底层原理掌握好,万变不离其宗
  • 面试官最爱

Event Loop

1、宏任务:MacroTask

  • script全部代码
  • setTimeOut
  • setInterval
  • setImmediate
  • I/O
  • UI Rendering

2、微任务:MicroTask

  • Process.nextTick(Node独有)
  • Promise
  • MutationObserver

浏览器中的Event Loop

JS有一个主线程main thread和调用栈call-stack,所有的任务哦度会被放到调用栈里等待主线程执行。

1、调用栈

采用后进先出的原则, 当函数执行的时候,会被添加到栈的顶部,当操作完成后,就会从栈顶移除,直到栈内被清空。

2、 同步任务和异步任务

JS单线程任务分为:同步任务 and 异步任务

同步任务:在调用栈中按顺序等待主线程依次执行

异步任务:在异步任务有结果后,将注册的回调函数放入任务队列中等待主线程空闲,被读取到栈内等待执行

任务队列Task Queue:队列,先进先出

3、事件循环的进程模型

  • 选择当前要执行的任务队列,选择任务队列中最先进入的任务,如果任务队列为空即null,则执行跳转到微任务的执行步骤
  • 将事件循环中的任务设置为已选择任务
  • 执行任务
  • 将事件循环中当前运行任务设置为null
  • 将已运行完成的任务从任务队列中删除
  • microtasks步骤:进入microtask检查点
  • 更新界面渲染
  • 返回第一步

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

  • 设置microtask检查点标志为true
  • 当事件循环microtask执行不为空时,选择一个最先进入的microtask队列的microtask,将事件循环的microtask设置为已选择的microtask并运行,将已执行完的microtask设置为null,移除microtask
  • 清理indexDB事务
  • 设置进入microtask检查点标志为false

执行栈完成同步任务后,查看执行栈是否为空,如果执行栈为空,就会检查微任务队列是否为空,如果是空的话,执行宏任务,否则就一次性执行完所有的微任务。

举个例子🌰

console.log('script start');

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

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});
console.log('script end');

第一次执行

执行同步代码,将宏任务和微任务划分到各自队列中

Tasks宏任务:run script、 setTimeout callback
Microtasks微任务:Promise then
JS stack: script

Log: script start、script end。	

第二次执行

执行宏任务后,检测到微任务队列中不为空,执行Promise1,执行完后调用Promise2.then,放入到微任务中,再执行调用Promise.then

Tasks宏任务:run script、 setTimeout callback
Microtasks微任务:Promise2 then
JS stack: Promise2 callback	

Log: script start、script end、promise1、promise2

第三次执行

当微任务队列为空时,执行宏任务,执行setTmeout callback,打印日志

Tasks宏任务:setTimeout callback
Microtasks微任务:	
JS stack: setTimeout callback

Log: script start、script end、promise1、promise2、setTimeout

第四次执行

清空Tasks队列和JS Stack

Tasks宏任务:setTimeout callback
Microtasks微任务:	
JS stack:

Log: script start、script end、promise1、promise2、setTimeout

浏览器中的Event Loop总结

  • 执行一个宏任务(栈中没有就从事件队列中获取)
  • 执行过程遇到微任务,添加到微任务队列中
  • 宏任务执行完毕后,立即执行微任务队列的所有微任务
  • 当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染
  • 渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

Node中的Event Loop

Node中的Event Loop是基于libuv实现的,而libuv是Node的新跨平台抽象层,libuv使用异步、事件驱动的编程方式,核心是提供i/o事件循环和异步回调。

一共分为6个阶段:

1、timers:执行setTimeout和setInterval中到期的callback

执行这两个回调需要设置一个毫秒数,由于system的调度可能会延时,达不到预期时间。

2、pending callback:上一轮循环中少数的callback会放在这一阶段执行

此阶段执行某些操作系统回调。

3、idle,prepare:仅在内部使用

4、poll:最重要的阶段,执行pengding callback,在适当的情况下会阻塞在这个阶段

主要的两个功能:

执行I/O回调、处理轮询队列中的事件

当事件循环进入poll阶段并且在timers中没有可执行的定时器,将发生:

如果poll队列不为空,则事件循环将遍历其同步执行它们的callback队列,直到队列为空,或者达到system-dependent(系统相关限制)。

如果队列为空,将发生:

  • 如果有setImmediate()回调需要执行,则会立即停止执行poll阶段并进入执行check阶段以执行回调
  • 如果没有setImmediate()回调,poll阶段将等待callback被添加到队列中,然后立即执行

5、check:执行setImmediate

此阶段运训人员在poll阶段完成后立即执行回调。

如果poll阶段闲置并且script已排队setImmediate(),则事件循环到达check阶段执行而不是继续等待。

setImmediate()是一个特殊的计时器,它在事件循环的一个单独阶运行,使用libuv API来调度在poll阶段完成后执行的回调。

通常,当代码被执行时,事件循环最终将达到poll阶段,它将等待传入连接,请求等。

但是如果已经调度了回调setImmediate(),并且轮询阶段变为空闲,则它将结束并且达到check阶段,而不是等到poll事件。

setImmediate() 的setTimeout()的区别

根据被调用的时间以不同的方式表现:

  • setImmediate()设计用于在当前poll阶段完成后check阶段执行脚本
  • setTimeout()安排在经过最小ms后运行的搅拌,在timers阶段执行
setTimeout(() => {
  console.log('timeout');
}, 0);

setImmediate(() => {
  console.log('immediate');
});

执行定时器的顺序将根据调用它们的上下文而有所不同。如果从主模块中调用两者,那么时间将收到进程性能的限制。其结果也不相同。

Process.nextTick()

Process.nextTick()虽然是异步api的一部分,单不是事件循环的一部分。

Process.nextTick()callback添加到next tick队列。一旦当前事件轮询队列的任务全部完成,在next tick队列中的所有callbacks会被依次调用。

也就是说,如果存在nextTick队列,就会清空队列中的所有回调函数,并且优先于其他microtask执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

椰卤工程师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值