关于javascript:js-中的-EventLoop

39次阅读

共计 3282 个字符,预计需要花费 9 分钟才能阅读完成。

js 中的 EventLoop

起始

EventLoop 是什么

JavaScript 有一个基于事件循环 (EventLoop) 的并发模型,事件循环负责执行代码、收集和处理事件以及执行队列中的子工作。> 浏览器和 NodeJS 基于不同的技术实现了各自的 Event Loop。

事件循环

之所以称之为 事件循环,是因为它常常依照相似如下的形式来被实现:

while (queue.waitForMessage()) {queue.processNextMessage();
}

queue.waitForMessage() 会同步地期待音讯达到(如果以后没有任何音讯期待被解决)。

事件

同步工作和异步工作

Javascript 单线程工作被分为同步工作和异步工作,同步工作会在调用栈中依照程序期待主线程顺次执行,异步工作会在异步工作有了后果后,将注册的回调函数放入工作队列中期待主线程闲暇的时候(调用栈被清空),被读取到栈内期待主线程的执行。

从图中能够看出, js 的指向都是会先指向同步代码, 碰到异步代码都是存入队列中, 等同步代码执行结束之后再依照肯定的规定执行

异步工作的分类

宏工作

macrotask,也叫 tasks。一些异步工作的回调会顺次进入 macro task queue,期待后续被调用,这些异步工作包含

  • setTimeout
  • setInterval
  • setImmediate (Node 独有)
  • requestAnimationFrame (浏览器独有)
  • I/O
  • UI rendering (浏览器独有)
微工作

microtask,也叫 jobs。另一些异步工作的回调会顺次进入 micro task queue,期待后续被调用,这些异步工作包含:

  • process.nextTick (Node 独有)
  • Promise
  • Object.observe
  • MutationObserver

这里只针对浏览器和 NodeJS

运行

运行原理:

  1. 运行同步工作, 微工作退出队列, 期待同步工作执行结束
  2. 查看是否有微工作, 若有则执行, 依照先进先出的规定进行
  3. 微工作完结后执行宏工作, 执行完 每一个 宏工作之后都会再次进入第 2 步流程
  4. 微工作和宏工作都执行结束

例子 1:

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');

执行后果:

script start
script end
promise1
promise2
setTimeout

剖析一下执行的过程:

  1. 执行了同步代码 console.log('script start');console.log('script end');
  2. 执行微工作 Promise , 打印 promise
  3. 执行宏工作 setTimeout
  4. 清空队列和栈堆

例子 2:

console.log(1);
setTimeout(() => {console.log(2); Promise.resolve().then(() => {console.log(3) });});
new Promise((resolve, reject) => {console.log(4) resolve(5)}).then((data) => {console.log(data);})
setTimeout(() => {console.log(6);})
console.log(7);

后果:

1
4
7
5
2
3
6

执行过程:

  1. 首先天然是执行同步代码: 几个 console, 对于 new Promise 须要留神的是, 他 new
    的时候, 并不是异步的, 回调函数才是异步工作, 所以打印的是: 1,4,7
  2. 执行微工作, promise 里的几个 then 回调函数, 所以打印 5
  3. 微工作临时执行结束, 执行宏工作, setTimeout, 打印 2, 这个时候又呈现了一个微工作退出了微工作的队列
  4. 一个宏工作执行结束了, 发现了新的同步工作和微工作, 开始执行, 打印 3, 微工作执行结束
  5. 执行下一个宏工作, 打印 6
  6. 全副执行结束, 打印程序是: 1,4,7,5,2,3,6

在执行微队列 microtask queue 中工作的时候,如果又产生了 microtask,那么会持续增加到队列的开端,也会在这个周期执行,直到 microtask queue 为空进行。 当然如果你在 microtask 中一直的产生 microtask,那么其余宏工作 macrotask 就无奈执行了,然而这个操作也不是有限的,拿 NodeJS 中的微工作 process.nextTick()来说,它的下限是 1000 个;

node 中的 EventLoop

— 援用自其它博客
Node 中的 Event Loop 是基于 libuv 实现的,而 libuvNode 的新跨平台形象层,libuv 应用异步,事件驱动的编程形式,外围是提供 i/o 的事件循环和异步回调。libuv 的 API 蕴含有工夫,非阻塞的网络,异步文件操作,子过程等等。Event Loop就是在 libuv 中实现的。

node 中的宏队列

宏队列的回调工作有如下 6 个阶段

各个阶段执行的工作如下:

  • timers 阶段:这个阶段执行 setTimeout 和 setInterval 预约的 callback
  • I/O callback 阶段 :执行除了 close 事件的 callbacks、被 timers 设定的 callbacks、setImmediate() 设定的 callbacks 这些之外的 callbacks
  • idle, prepare 阶段:仅 node 外部应用
  • poll 阶段:获取新的 I / O 事件,适当的条件下 node 将阻塞在这里
  • check 阶段 :执行 setImmediate() 设定的 callbacks
  • close callbacks 阶段 :执行 socket.on(‘close’, ….) 这些 callbacks

NodeJS 中宏队列次要有 4 个
由下面的介绍能够看到,回调事件次要位于 4 个 macrotask queue 中:

  1. Timers Queue
  2. IO Callbacks Queue
  3. Check Queue
  4. Close Callbacks Queue

这 4 个都属于宏队列,然而在浏览器中,能够认为只有一个宏队列,所有的 macrotask 都会被加到这一个宏队列中,然而在 NodeJS 中,不同的 macrotask 会被搁置在不同的宏队列中。
NodeJS 中微队列次要有 2 个

  1. Next Tick Queue:是搁置 process.nextTick(callback)的回调工作的
  2. Other Micro Queue:搁置其余 microtask,比方 Promise 等

大体解释一下 NodeJS 的 Event Loop 过程:

  1. 执行全局 Script 的同步代码
  2. 执行 microtask 微工作,先执行所有 Next Tick Queue 中的所有工作,再执行 Other Microtask Queue 中的所有工作
  3. 开始执行 macrotask 宏工作,共 6 个阶段,从第 1 个阶段开始执行相应每一个阶段 macrotask 中的所有工作,留神,这里是所有每个阶段宏工作队列的所有工作,在浏览器的 Event Loop 中是只取宏队列的第一个工作进去执行,每一个阶段的 macrotask 工作执行结束后,开始执行微工作,也就是步骤 2
  4. Timers Queue -> 步骤 2 -> I/O Queue -> 步骤 2 -> Check Queue -> 步骤 2 -> Close Callback Queue -> 步骤 2 -> Timers Queue ……
  5. 这就是 Node 的 Event Loop

总体来说运行机制是和浏览器外面差不多的 也是先同步代码, 再一步微工作, 宏工作, 微工作 … 这样
惟一不同点就是宏工作是一系列动作, 在第一个动作结束后就会去查看微工作

援用文章:
https://segmentfault.com/a/11…
https://developer.mozilla.org…
https://zhuanlan.zhihu.com/p/…

正文完
 0