参考资料:The Node.js Event Loop, Timers, and process.nextTick()
什么是事件循环?
事件循环允许nodejs完成非阻塞的IO操作(尽管JavaScript是单线程的)。
简单来说,在nodejs中,遇到IO操作或者网络连接等阻塞性的行为的时候,将这个操作交给nodejs底层的线程池去处理,而不会阻塞主线程。当线程池中的任务完成之后,会将结果和回调函数推入到事件队列中,主线程完成当前正在执行的脚本之后(IO中指定的回调函数),会去检查事件队列并执行其中的回调,直到事件队列为空。
这只是一个大概的概述,接下来解释事件队列的细节
事件循环的模型
当nodejs启动的时候会初始化事件循环,在执行的第一个脚本中可能会执行异步的操作,然后就会处理事件循环。
下面是Node事件循环的模型图,事件循环中的操作按照途中所给的顺序执行。图中的每一个盒子都可以看成事件循环中的一个阶段。
┌───────────────────────────┐
┌─>│ timers │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ pending callbacks │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
│ │ idle, prepare │
│ └─────────────┬─────────────┘ ┌───────────────┐
│ ┌─────────────┴─────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └─────────────┬─────────────┘ │ data, etc. │
│ ┌─────────────┴─────────────┐ └───────────────┘
│ │ check │
│ └─────────────┬─────────────┘
│ ┌─────────────┴─────────────┐
└──┤ close callbacks │
└───────────────────────────┘
在事件循环的每一个阶段,都有一个先入先出(FIFO)队列,用来存放要执行的回调函数。当事件循环进入到某一个阶段的时候,按照队列的顺序执行队列中的所有回调函数,直到当前队列中的所有回调函数都被执行或者达到了回调函数的执行限制(如果在poll阶段一直有任务执行,那么timer阶段的任务就可能用于不会执行了,nodejs中为了避免这种其他阶段过于“饥饿”的情况,底层的libev有一个限制,达到限制之后不会向事件循环中添加更多的任务)。
之后会切换到下一个阶段,执行该阶段的任务队列。
实例:
const fs = require('fs');
function someAsyncOperation(callback)