Event Loop 是事件循环啊,是指浏览器或Node的一种解决javaScript单线程运行时不会阻塞的一种机制,也就是我们经常使用异步的原理。
1、主线程开始执行一段代码,假设开始执行一个script标签内的代码,将代码放入执行栈中执行,统统不代码优先执行,执行过程中,当遇到任务源时,判断是宏任务还是微任务。
常见的宏任务和微任务
*宏任务:
包括script全部代码、setTimeout、setInterval、setImmediate(Node.js)、requestAnimationFrame(浏览器)、I/O
操作、UI渲染(浏览器),这些代码执行便是宏任务。
微任务:
process.nextTick(Node.js)、promise、mutationObserver
2.如果是宏任务,加入到宏任务队列中,如果是微任务,加入到微任务队列中;
3.同步代码执行完成,执行栈空闲,检查微任务队列中是否有可执行任务,如果有,一次执行所有微任务队列中的任务。若果没有,当前任务执行结束;
4、渲染UI
5、检查宏任务队列是否有可执行的宏任务,如果有,取出队列中最前面的那个宏任务,加入到执行栈中开始执行,然后重复以上步骤。知道宏任务队列中所有任务执行结束。
实例代码:
const promise =new Promise((resolve,reject)=>{
resolve("success1")
reject("error")
resolve("success2")
})
promise.then(res=>{
console.log("then:",res);
}).catch(err=>{
console.log("catch",err);
})
node中的Event Loop是基于libuv实现的,而libuv是Node的新跨平台抽象层,libuv使用异步,事件驱动的编程方式,核心是提供i/o的时间循环和异步回调。libuv得API包含时间,非阻塞的网络,异步文件操作,子进程等等。Event Loop就是在libuv中实现的。
上图中,用户输入JavaScript代码,由V8引擎进行解析,V8调用Node API然后由libuv进行处理,libuv提供Event Loop来处理各类任务,处理完成后将结果返回给V8,V8再将结果返回给用户。
*浏览器的Event Loop只分了两层优先级,一层是宏任务,一层是微任务。但是宏任务与微任务之间没有再划分优先级。
*node.js宏任务之间是有优先级的。
可以把宏任务队列拆成5个优先级(有些说是6个,多一个idle,prepre(仅在内部使用))Timers、Pending、Poll、Check、Close。
timers Callback:设计到时间,肯定越早执行越准确,所以这个优先级最高很容易理解。
Pending Callback:处理网络、IO等异常时的回调,有的系统会等待发生错误的上报,所以得处理下。
Poll Callback:处理IO的data,网络的connection,服务器主要处理的就是这个。
Check Callback:执行setImmediate的回调,特点是刚执行完IO之后就能回调这个。
Close Callback:关闭资源的回调,晚点执行影响也不到,优先级最低。
浏览器环境下,microtask的任务队列是每个macrotask执行完之后才执行的。而在Node.js中,microtask会在实践循环的各个几阶段之间执行,也就是一个阶段执行完毕,就会去执行microttask队列的任务。
Node.js的Event Loop并不是浏览器那种一次执行一个宏任务,然后执行所有的微任务,而是执行完一定数量的Times宏任务,再去执行所有微任务,然后再执行一定数量的Pending的宏任务,然后再去执行所有微任务,剩余的Poll、Check、Close的宏任务也是这样的。
浏览器和Node环境下,microtask任务队列的执行时机不同
*Node端,microtask在事件循环的各个阶段之间执行
*浏览器端,microtask在事件循环的macrotask执行晚安之后执行。