组内每周都会有分享总结会,昨晚分享的课题是Event Loop
,我很积极且有点自信地答了几道题,结果被虐的体无完肤
,果真有些东西不常常回顾就容易忘,因而花一夜挑灯夜战
从新作了一份有关event loop
的知识总结,在此分享给你们,但愿对各位看官有所帮助,看完有收获的同窗还请积极点赞,讲的不对的地方,望指出,我会及时修正,谢谢~javascript
浏览器端的event loop基于javascript中的
堆/栈/任务队列
,任务队列又分为宏任务
与微任务
java
相同做用域下
,会先执行微任务,再执行宏任务宏任务处于微任务做用域下
,会先执行微任务,再执行微任务中的宏任务微任务处于宏任务做用域下时
,会先执行宏任务队列中的任务,而后再执行微任务队列中的任 务,在当前的微任务队列没有执行完成时,是不会执行下一个宏任务的。本文主要讲解的仍是Node,对于浏览器端event loop的具体分析及证实能够查看这篇文章探究javascript中的堆/栈/任务队列与并发模型 event loop的关系node
6个阶段
,每一个阶段都有1个任务队列,微任务在事件循环的各个阶段之间执行一个timer
指定一个下限时间而不是准确时间,在达到这个下限时间后执行回调。在指定的时间事后,timers
会尽早的执行回调,可是系统调度或者其余回调的执行可能会延迟它们。git
从技术上来讲,
poll
阶段控制timers
何时执行,而执行的具体位置在timers
。 下限的时间有一个范围:[1, 2147483647],若是设定的时间不在这个范围,将被设置为1。github
poll阶段有两个主要的功能:
1. 是执行下限时间已经达到的timers的回调
2. 是处理poll队列里的事件。promise
注:Node不少API都是基于
事件订阅
完成的,这些API的回调应该都在poll
阶段完成。 当事件循环进入poll阶段:浏览器
poll队列不为空的时候,事件循环确定是先遍历队列并同步执行回调,直到队列清空或执行回调数达到系统上限。网络
poll队列为空的时候,这里有两种状况。并发
若是代码已经被setImmediate()设定了回调,那么事件循环直接结束poll阶段进入check阶段来执行check队列里的回调。socket
若是代码没有被设定setImmediate()设定回调:
这个阶段容许在poll阶段结束后当即执行回调。若是poll阶段空闲,而且有被setImmediate()设定的回调,那么事件循环直接跳到check执行而不是阻塞在poll阶段等待回调被加入。
注:事件循环运行到
check
阶段的时候,setImmediate()
具备最高优先级,只要poll
队列为空,代码被setImmediate()
,不管是否有timers
达到下限时间,setImmediate()
的代码都先执行
若是一个socket
或handle
被忽然关掉(好比socket.destroy()
),close事件将在这个阶段被触发,不然将经过process.nextTick()
触发。
setTimeout(()=>{
console.log('timer')
})
setImmediate(()=>{
console.log('immediate')
})
复制代码
首先进入的是timers
阶段,若是咱们的机器性能通常,那么进入timers阶段,一毫秒已通过去了,那么setTimeout的回调会首先执行。
若是没有到一毫秒,那么在timers阶段的时候,下限时间没到,setTimeout回调不执行,事件循环来到了poll阶段,这个时候队列为空,此时有代码被setImmediate(),因此进入check阶段,先执行了setImmediate()的回调函数,以后在下一个事件循环再执行setTimemout的回调函数。
而咱们在执行代码的时候,进入timers的时间延迟实际上是随机的,并非肯定的,因此会出现两个函数执行顺序随机的状况。
fs.readFile('./main.js',()=>{
setTimeout(()=>{
console.log('timer')
})
setImmediate(()=>{
console.log('immediate')
})
})
复制代码
setImmediate
永远先于
setTimeout
执行
fs.readFile
的回调是在poll
阶段执行的,回调执行完毕后poll阶段的队列为空,因而进入check
阶段,执行setImmediate
回调,而setTimeout
的回调须要等到下一个事件循环的timers
阶段才去执行
对于这两个,咱们能够把它们理解成一个微任务。也就是说,它其实不属于事件循环的一部分。
那么他们是在何时执行呢?
无论在什么地方调用,他们都会在其所处的事件循环最后,在事件循环进入下一个循环的阶段前执行,可是
nextTick
优先于promise
执行。process.nextTick()
会在各个事件阶段之间执行,一旦执行,要直到nextTick
队列被清空,才会进入到下一个事件阶段,因此若是递归调用process.nextTick()
/promise
,会致使出现I/O starving(饥饿)的问题,推荐使用setImmediate()
setTimeout(() => {
console.log('timeout1')
Promise.resolve().then(()=>{
console.log('reslove1')
})
}, 0)
setTimeout(() => {
console.log('timeout2')
Promise.resolve().then(()=>{
console.log('reslove2')
})
}, 0)
setImmediate(()=>{
console.log('setImmediate1')
})
setImmediate(()=>{
console.log('setImmediate2')
})
复制代码
setTimeout(() => {
console.log('timeout1')
Promise.resolve().then(()=>{
console.log('reslove1')
})
}, 0)
setTimeout(() => {
console.log('timeout2')
Promise.resolve().then(()=>{
console.log('reslove2')
})
}, 0)
setImmediate(()=>{
console.log('setImmediate1')
})
setImmediate(()=>{
console.log('setImmediate2')
})
Promise.resolve('resolve3').then((data)=>{
console.log(data)
})
复制代码
篇幅很长,很是感受你看完了个人文章,谢谢~,答案会公布在issue