Nodejs 特点
Nodejs 是非阻塞I/O、事件与回调函数、单线程、跨平台
Nodejs 执行流程
Nodejs 启动后,它初始化事件循环、执行当前代码、然后进入事件循环队列
Nodejs 是通过一个单线程的调用堆栈来执行代码的,这里代码会分为立即执行的代码和有异步操作的代码。立即执行的代码会直接进入调用堆栈执行,有异步操作的代码进入调用堆栈执行后会把这些异步操作的回调加入独立的队列,在进入事件循环后会检测这些回调队列是否触发。
有异步操作的代码有:
- setTimeout
- setInterval
- setImmediate
- nextTick
- queueMicroTask
- promise
- fs.open...
- http.on('connection'...
上述这些异步操作可以分为:MicroTask Queue、NextTick Queue、Message Queue、Poll Queue、Immediate Queue
- MicroTask Queue 包括 queueMicroTask 和 promise
- NextTick Queue 包括 nextTick
- Message Queue 包括 setTimeout setInterval
- Poll Queue 包括 fs.open http.on('connection'... 等I/O操作
- Immediate Queue 包括 setImmediate
Event Loop 阶段
Event Loop 是 Nodejs 处理非阻塞I/O的机制
它分为6个阶段,timer -> pending -> idle -> poll -> check -> close,事件循环按这个顺序检测执行各个阶段的回调队列,当前队列清空后进入下一阶段。
- timer 定时器阶段,执行 Message Queue 的回调
- pending 挂起回调,ECONREFUSE
- idle 系统内部使用
- poll 轮询阶段,执行 Poll Queue 操作的回调
- check 检查阶段,执行 Immediate Queue 的回调
- close 关闭阶段,执行关闭相关的回调,socket.on('close'...
轮询阶段(Poll)
两点作用,一是计算应该阻塞和轮询的I/O的时间、二是处理轮询队列的回调
当事件循环进入 轮询 阶段且 没有被调度的计时器时 ,将发生以下两种情况之一:
- 如果 轮询 队列 不是空的,事件循环将循环访问回调队列并同步执行它们,直到队列已用尽,或者达到了与系统相关的硬性限制。
- 如果 轮询 队列 是空的 ,还有两件事发生:
- 如果脚本被 setImmediate() 调度,则事件循环将结束 轮询 阶段,并继续 检查 阶段以执行那些被调度的脚本。
- 如果脚本 未被 setImmediate()调度,则事件循环将等待回调被添加到队列中,然后立即执行。
- 一旦 轮询 队列为空,事件循环将检查已达到时间阈值的计时器。如果一个或多个计时器已准备就绪,则事件循环将绕回计时器阶段以执行这些计时器的回调。
微任务(MicroTask Queue)
MicroTask Queue 也不在事件循环的阶段中,它是在每次调用堆栈执行完成后立即执行。
微任务队列在执行时,如果回调中又有新加入的微任务会立即执行新加入的微任务回调。
在当前函数结束之前解析的 Promise 将在当前函数之后立即执行。
NextTick Queue
NextTick queue 不在事件循环的队列中,它是独立存在的队列,它和微任务一样都是尽可能快的执行。它会在每个事件循环的阶段完成后都会尝试检查执行。
我们指示引擎在当前操作结束时调用此函数,在下一个事件循环滴答开始之前:
三个队列
Nodejs 的执行包括三部分,调用堆栈、微任务队列、循环队列,先执行调用堆栈,调用堆栈清空后,检查微任务队列,有则执行微任务队列,然后进入事件循环,检查各个阶段的队列,存在则放入调用堆栈执行,直到循环队列为空,程序结束。
网友评论