在 node 11 版本中,node 下 Event Loop 已经与浏览器趋于相同。
在 node 11 版本中,node 下 Event Loop 已经与浏览器趋于相同。
在 node 11 版本中,node 下 Event Loop 已经与浏览器趋于相同。
Event Loop也是js老生常谈的一个话题了。2月底看了阮一峰老师的《Node定时器详解》一文后,发现没法彻底对标以前看过的js事件循环执行机制,又查阅了一些其余资料,记为笔记,感受不妥,总结成文。html
浏览器中与node中事件循环与执行机制不一样,不可混为一谈。
浏览器的Event loop是在HTML5中定义的规范,而node中则由libuv库实现。同时阅读《深刻浅出nodeJs》一书时发现比较当时node机制已有不一样,因此本文node部分针对为此文发布时版本。强烈推荐读下参考连接中的前三篇。html5
js执行为单线程(不考虑web worker),全部代码皆在执行线程调用栈完成执行。当执行线程任务清空后才会去轮询取任务队列中任务。node
异步任务分为task(宏任务,也可称为macroTask)和microtask(微任务)两类。
当知足执行条件时,task和microtask会被放入各自的队列中等待放入执行线程执行,咱们把这两个队列称为Task Queue(也叫Macrotask Queue)和Microtask Queue。git
即为同步完成,一个宏任务,全部微任务,一个宏任务,全部微任务......github
new Promise((resolve, reject) =>{console.log(‘同步’);resolve()}).then(() => {console.log('异步')})
,即promise
的then
和catch
才是microtask,自己的内部代码不是。while (true) { 宏任务队列.shift() 微任务队列所有任务() }
js执行为单线程,全部代码皆在执行线程调用栈完成执行。当执行线程任务清空后才会去轮询取任务队列中任务。web
在node中事件每一轮循环按照顺序分为6个阶段,来自libuv的实现:api
除上述循环阶段中的任务类型,咱们还剩下浏览器和node共有的microtask和node独有的process.nextTick
,咱们称之为Microtask Queue和NextTick Queue。promise
咱们把循环中的几个阶段的执行队列也分别称为Timers Queue、I/O Queue、Check Queue、Close Queue。浏览器
在进入第一次循环以前,会先进行以下操做:app
process.nextTick()
按照咱们的循环的6个阶段依次执行,每次拿出当前阶段中的所有任务执行,清空NextTick Queue,清空Microtask Queue。再执行下一阶段,所有6个阶段执行完毕后,进入下轮循环。即:
能够看出,nextTick
优先级比promise
等microtask高。setTimeout
和setInterval
优先级比setImmediate
高。
setImmediate
则会在此轮循环的check阶段执行,若是在timers阶段建立了setTimeout
,因为timers已取出完毕,则会进入下轮循环,check阶段建立timers任务同理。setTimeout
优先级比setImmediate
高,可是因为setTimeout(fn,0)
的真正延迟不可能彻底为0秒,可能出现先建立的setTimeout(fn,0)
而比setImmediate
的回调后执行的状况。while (true) { loop.forEach((阶段) => { 阶段所有任务() nextTick所有任务() microTask所有任务() }) loop = loop.next }
function sleep(time) { let startTime = new Date() while (new Date() - startTime < time) {} console.log('1s over') } setTimeout(() => { console.log('setTimeout - 1') setTimeout(() => { console.log('setTimeout - 1 - 1') sleep(1000) }) new Promise(resolve => resolve()).then(() => { console.log('setTimeout - 1 - then') new Promise(resolve => resolve()).then(() => { console.log('setTimeout - 1 - then - then') }) }) sleep(1000) }) setTimeout(() => { console.log('setTimeout - 2') setTimeout(() => { console.log('setTimeout - 2 - 1') sleep(1000) }) new Promise(resolve => resolve()).then(() => { console.log('setTimeout - 2 - then') new Promise(resolve => resolve()).then(() => { console.log('setTimeout - 2 - then - then') }) }) sleep(1000) })
浏览器输出:
setTimeout - 1 //1为单个task 1s over setTimeout - 1 - then setTimeout - 1 - then - then setTimeout - 2 //2为单个task 1s over setTimeout - 2 - then setTimeout - 2 - then - then setTimeout - 1 - 1 1s over setTimeout - 2 - 1 1s over
node输出:
setTimeout - 1 1s over setTimeout - 2 //一、2为单阶段task 1s over setTimeout - 1 - then setTimeout - 2 - then setTimeout - 1 - then - then setTimeout - 2 - then - then setTimeout - 1 - 1 1s over setTimeout - 2 - 1 1s over
由此也可看出事件循环在浏览器和node中的不一样。
???