异步、轮询

javaScript

在js执行的过程中分为同步任务和异步任务,为了协调异步异步任务,Node提供了5个定时器让任务可以在指定的时间运行。

setTimeOut()
setInterval()
setImmediate()
process.nextTick()
Promise.resolve()

同步任务总是比异步任务更早的执行,而异步任务又分为以下两种。
1)、追加在本轮循环的异步任务
2)、追加在次轮循环的异步任务

上面的两种异步任务,其中本轮循环异步一定早于次轮循环异步,在node中process.nextTickPromise的回调函数追加到本轮循环,即同步任务完成就立即执行,而setTimeoutsetIntervalsetImmediate的回调函数,追加在次轮循环。

在本轮循环中Promise会进入到微任务队列中,执行完process.nextTick队列后就开始执行Promise任务队列。

   ┌───────────────────────┐
┌─>│        timers         │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐    
│  │     idle, prepare     │
│  └──────────┬────────────┘      ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │
│  │         poll          │<─────┤  connections, │
│  └──────────┬────────────┘      │   data, etc.  │
│  ┌──────────┴────────────┐      └───────────────┘
│  │        check          │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
└──┤    close callbacks    │
   └───────────────────────┘

event loop的每一个循环都需要一次经过上述的阶段,而每个阶段都有自己的callback队列,每当进入某个阶段,都会从属的队列中取出callback来执行,当队列为空或者被执行callback数量达到系统最大数量时,进入下一个阶段,这6个阶段都执行完毕称为一轮循环。

由于setTimeout处于次循环的timers阶段,而setImmediate处于check阶段,所有setTimeout比setImmediate执行。

示例

setImmediate早于setTimeout执行。
const fs= require('fs')
fs.readFile('test.js', () => {
  setTimeout(() => cosole.log(1))
  setImmediate(() => console.log(2))
})

// 2、1

fs.readFile()完成文件读取周,会将回调放入poll队列并执行,poll执行回调后,setImmediate与setTimeout被放入各自的队列,poll阶段结束后执行check阶段,所有setImmediate早于setTimeout。

本轮循环顺序
process.nextTick(() => console.log(1));
Promise.resolve().then(() => console.log(2));
process.nextTick(() => console.log(3));
process.resolve().then(() => console.log(4));

// 1、3、2、4

只有一个队列全部清空后才会执行下一个队列。

参考文章