JavaScript < ES5、ES6、ES7、… >

nodejs Event Loop

2019-02-01  本文已影响9人  sweetBoy_9126
1.前置知识

比如:
对于一个ajax请求get('/1.json'),若果这个请求需要100ms,那么我在这100ms内触发一个点击事件让它打印出一个1可以吗?
答:可以,虽然说js是单线程,但是它可以主要是因为它的请求让它的兄弟网络模块去做了,这时候js就处于空闲状态,等到网络模块请求完成就会通知js,js然后就会去执行一个回调

2.nodejs Event loop

2.1 当nodejs启动时,会执行三件事情

  1. 初始化event loop
  2. 开始执行脚本
  3. 进入event loop

2.2. 处理event loop经历的几个阶段(初始化的时候不会有这几个阶段)

上面的几个阶段中我们只需要了解红框中的三个就可以了,并且每一个阶段都有自己的一个队列

  • timers阶段
    处理setTimeout和setInterval中到时的回调函数,对于timers中队列的处理,setTimeout或setInterval中的函数都添加到队列里,同时记下来这些函数什么时间被调用到了调用时间就调用,没到时间就进入下一阶段,然后会停留在poll阶段
  • poll阶段
    轮询阶段
    比如上面说的让兄弟(其他线程处理的请求任务),在这个阶段会一直去问执行相关任务的模块任务执行完了没有,一旦任务执行完成,相关的数据就会放到一个回调里面然后加入到poll的队列中,也就是除了timers阶段外的所有回调都是在poll这个阶段处理的。poll阶段会一直重复检查刚才timers里没有到时间的计时器有没有到时间,如果到时间了,就直接通过check到达timers阶段通知timers执行这个回调,然后从timers队列中清除
    check阶段
    只处理setImmediate

2.3. 通过一个例子更加清楚的理解timers是怎么执行的

比如一开始有两个计时器一个4ms一个100ms
正常情况:当第一次timers的时候4ms已经到了所以直接执行里面的函数然后从队列中清除,然后第二个100ms并没有到,就会进入poll阶段停留,poll阶段会一直读时间问100ms有没有到,如果到了就直接通过check到timers然后通知它100ms到了执行相关的回调,从队列中清除
特例:因为poll从4ms一直等到100ms所以这段空闲的时间如果有另一个请求执行完了比如说是在90ms执行完了,那么这个时候它会去执行这个回调,但是这个函数调用也是需要时间的如果说调用用了20ms那么也就是说会在110ms的时候处理完这个函数的调用,也就是会错过100ms这个时间点,那么就会在110ms的时候通过check通知timers执行100ms那个回调

2.4. 关于setTImeout和setImmediate的执行顺序
答:正常情况下是setImmediate先执行,只有第一次启动nodejs的情况下timers里的定时器到时间了才可能setTimeout先执行。

原因:因为nodejs启动会执行三件事,开始执行脚本也就是执行你页面的代码,这时候会执行你的setTImeout,然后才会去处理event loop,而从执行脚本到处理event loop中间也需要时间,setTimeout最小的时间是4ms,如果从执行脚本到处理event loop花了5ms时间,那么一进入timers就会发现时间到了就会立刻处理回调,所以这时候setTimeout就会先执行

nextTick

进入每个阶段前都会执行,包括nodejs启动的时候也会执行

setTimeout(()=>{
  console.log('timeout')
})
setImmediate(()=>{
  console.log('immediate')
})
process.nextTick(()=>{
  console.log('next tick')
})
//next tick
//timeout
//immediate
Macro Task(宏任务)&Micro Task(微任务)

妈Ma 咪Mi
先执行完一个宏任务再去执行一个微任务,微任务每次都要清空
setTimeout后面的回调还有当前的脚本代码都是宏任务
nextTick和Promise都属于微任务

上图中setTimeout里面的回调是宏任务所以会放到宏任务的下一个任务队列中,而执行完一个宏任务后紧接着要执行一个微任务promise里面的是微任务,所以接着执行的是2,最后执行3

重要题目:

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

let promise = new Promise((resolve, reject)=>{
    console.log(1)
    resolve()
})
promise.then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

上面代码执行顺序是?
答:
script start
1
promise1
promise2
setTimeout
原因:promise里面的代码时立即执行的所以在script start后紧接着是1,而setTImeout里面的回调是宏任务,当前代码就是宏任务,所以需要在下个微任务后执行,promise.then里面的代码都是微任务,所以会先pormise最后setTimeout

上一篇 下一篇

猜你喜欢

热点阅读