Event Loop
Event Loop 翻译过来叫 事件循环. 分为:
- 浏览器 Event Loop
- node Event Loop
浏览器 Event Loop
在讲浏览器 Event Loop之前,我们需要了解宏任务与微任务 以及 执行栈stack; 堆heap;队列callback queue的含义。
执行栈 stack:
execution stack 所有的代码都是在此空间中执行的。
堆 heap
对象被分配在堆中,堆是一个用来表示一大块(通常是非结构化的)内存区域的计算机术语。
任务队列callback queue
所有的任务可以分为同步任务和异步任务,同步任务,顾名思义,就是立即执行的任务,同步任务一般会直接进入到主线程中执行;而异步任务,就是异步执行的任务,比如ajax网络请求,setTimeout定时函数等都属于异步任务,异步任务会通过任务队列(Event Queue)的机制来进行协调。
一个 JavaScript 运行时包含了一个待处理消息的消息队列。每一个消息都关联着一个用以处理这个消息的回调函数。
其中:任务队列放着宏任务1,宏任务2...宏任务n;微任务1,微任务2...微任务n;
js是单线程执行的,是因为保证只能由一个线程去操作DOM。
Event Loop 流程
一个Event Loop有一个或多个宏任务队列, 有一个微任务队列。
通过上图我们可以看出,事件循环的过程:
- 执行初始化(script全局执行)代码, 将异步事件回调函数交给对应模块(线程)管理。
- 当事件被触发时, 管理模块(线程)会将回调函数及其数据添加到对应的回调列队中。
- 只有当初始化代码(script全局执行代码,也被称为宏任务)执行完后(可能要一定时间), 将任务队列中的微任务队列里的微任务依次执行(清空);
- 当微任务队列中的所有微任务执行完成之后,再去取出任务队列中的下一个宏任务进行执行。
由于宏任务或微任务再执行的过程中又会产生新的异步事件回调,这些回调又会被放入任务队列中。如此往复循环。称这个过程为事件循环
node的Event Loop
Event Loop分为六个阶段:
- timer:执行timer(setTimeout, setInterval)的回调
- pending callbacks:系统操作的回调
- idle,pepare:内部使用
- poll: 等待新的I/O事件
- check:执行setImmediate回调
- close callbacks:内部使用,会执行一个soket.onclose()的操作
每一个节点都有一个callbacks的先进先出的队列需要执行。当event loop运行到一个指定阶段时,该阶段的所有callbacks队列会被执行,当队列callbacks执行完或者执行的callback数量超过该阶段的上限时, event loop 会转入下一个阶段。
其中我们关心的阶段主要是 poll阶段, timer阶段, check阶段。
node event Loop主要流程
- 代码进入poll阶段
- 判断poll队列为空或受到限制,false->执行poll队列中的callback.
- 判断poll队列为空或受到限制, true->是否设置了setImmedidate callback true->进入check阶段。
- 是否设置了setImmedidate callback false->等待callback加入poll队列; 判断timer队列中的回调是否到时 true->进入timer阶段
- 判断timer队列中的回调是否到时 false->等待callback加入poll队列;
如下图:
示例
const fs = require("fs"); //进入poll
fs.readFile(__filename, () => {
setTimeout(()=>{
console.log("settimeout");
}, 0);
setImmediate(()=>{
console.log("setImmediate");
});
})
从上图可以看出check callback会比timer队列先执行。所以上面结果输出顺序:
setImmediate , settimeout
process.nextTick()
是一个异步的node API, 但不属于event loop的阶段。
当你调用process.nextTick()时,event loop会停下来(被阻塞)先去把process.nextTick()的回调函数执行之后,再去执行事件循环。
示例
const fs = require("fs");
fs.readFile(__filename, () => {
setTimeout(()=>{
console.log("settimeout");
}, 0);
setImmediate(()=>{
console.log("setImmediate");
process.nextTick(()=>{
console.log("process.nextTick1");
});
});
process.nextTick(()=>{
console.log("process.nextTick2");
});
})
上面代码的结果输出顺序为:
process.nextTick2, setImmediate , process.nextTick1 , settimeout