Node.js 将各种函数( 也叫任务或回调 )分成至少 6 类,按先后顺序调用,因此将时间分为六个阶段:

  1. timers 阶段(setTimeout/setInterval)
  2. I/O callbacks 该阶段不用管
  3. idle, prepare 该阶段不用管
  4. poll 轮询阶段,停留时间最长,可以随时离开。
    1. 主要用来处理 I/O 事件,该阶段中 Node 会不停询问操作系统有没有文件数据、网络数据等
    2. 如果 Node 发现有 timer 快到时间了或者有 setImmediate 任务,就会主动离开 poll 阶段
  5. check 阶段,主要处理 setImmediate 任务
  6. close callback 该阶段不用管

Node.js 会不停从 1 ~ 6 循环处理各种事件,这个过程叫做事件循环(Event Loop)

nextTick

process.nextTick(fn) 的 fn 会在什么时候执行?

  • 在 Node.js 11 之前,会在每个阶段的末尾集中执行(俗称队尾执行)
  • 在 Node.js 11 之后,会在每个阶段的任务间隙执行(俗称插队执行)

浏览器跟 Node.js 11 之后的情况类似。
nextTick 只在 Node.js 中有
浏览器可用 window.queueMicrotask 模拟 nextTick

  1. setTimeout(() => console.log('timeout1'));
  2. setTimeout(() => {
  3. console.log('timeout2')
  4. // Node.js 要换成 process.nextTick
  5. window.queueMicrotask(() => {
  6. console.log('nextTick')
  7. })
  8. });
  9. setTimeout(() => console.log('timeout3'));
  10. setTimeout(() => console.log('timeout4'));
  11. // Node.js 11 之前结果是:timeout1 timeout2 timeout3 timeout4 nextTick
  12. // Node.js 11 之后及浏览器:timeout1 timeout2 nextTick timeout3 timeout4

Promise.then

Promise.resolve(1).then(fn) 的 fn 会在什么时候执行?
这要看 Promise 源码是如何实现,一般都是用 process.nextTick(fn) 实现的,所以直接参考 nextTick.

async / await

这是 Promise 的语法糖,所以直接转为 Promise 写法即可

实践
  1. 下面代码的执行顺序 ```javascript setTimeout(() => { console.log(‘setTimeout’) })

setImmediate(() => { console.log(‘setImmediate’) })

// 在 Node.js 中, 不能确定上面的输出顺序 // EventLoop 是用 C++ 写的,这取决于 C++ 和 JS 哪个先启动

  1. setImmediate 在浏览器中无效,可用改用 window.queueMicrotask, 微任务比宏任务先执行
  2. ```javascript
  3. setTimeout(() => {
  4. console.log('setTimeout')
  5. })
  6. window.queueMicrotask(() => {
  7. console.log('setImmediate')
  8. })
  9. // setImmediate setTimeout
  1. 下面代码的输出顺序 ```javascript async function async1() { console.log(‘1’) await async2() console.log(‘2’) }

async function async2() { console.log(‘3’) }

console.log(‘4’)

setTimeout(function(){ console.log(‘5’) }, 0)

async1()

new Promise(function(resolve){ console.log(‘6’) resolve() }).then(function(){ console.log(‘7’) })

console.log(‘8’)

``` 解析:

  1. 函数定义直接跳过,先执行 console.log('4')
  2. setTimeout 放入 timer 队列等待执行 timer = ['5']
  3. 执行 async1 函数
    1. 执行 console.log('1')
    2. await async2(); console.log('2') 等价于 async2().then( console.log('2') ), 即 await 后的代码都放到 .then 内,先执行 async2 函数,console.log('3'),.then 中的 console.log('2') 放入 nextTick 队列 next = ['2']
  4. Promise 中的函数 立刻执行,故执行 console.log('6'),.then 中的 console.log('7') 放入 nextTick 队列, next = ['2', '7']
  5. 执行 console.log('8')
  6. 此时 poll 完毕,优先执行 nextTick,输出 2, 7
  7. 然后进入 timer ,输出 5
  8. 结果为 4,1,3,6,8,2,7,5