您的位置:首页 > Web前端 > Node.js

深入理解nodejs Event loop

2017-08-02 17:29 615 查看

什么是事件循环

Event loop 允许node.js执行非阻塞I/O操作(尽管JS是单线程的),也就是在相应情况下,尽可能的将任务交给系统内核。

多数内核是多线程的,可以同时处理多个任务。当其中一个任务完成时,相应的callback被插入到轮询队列中,最终被执行。

事件循环

事件循环包含几个阶段,相应的阶段做相应的事

初始化: Node.js启动后,会进行一些初始化

初始化Event loop

处理目标脚本

然后进入事件循环



每个阶段,都有其FIFO队列,用来执行回调函数。

每个阶段都是特殊的。

当进行到该阶段时,会执行该阶段特有的操作,然后执行该阶段队列中的回调。

当队列空,或者达到执行次数限制,事件循环进行下阶段。

循环往复。

阶段总览

timers:执行setTimeout()和setInterval安排的回调

I/O callbacks: 执行几乎所有异常的close回调,由timer和setImmediate执行的回调。

idle,prepare: 只用于内部

poll : 获取新的I/O事件,node在该阶段会适当的阻塞

check : setImmediate的回调被调用

close callbacks: e.g socket.on(‘close’,…);

在每次运行事件循环之间,node.j检查是否有正在等待的异步i/o调用、timers等。如果没有,就清除并结束(退出程序),例如:执行一个程序,仅有一句话(var a= ‘hello’;),处理完目标代码后,不会进入evetloop,而是直接结束程序。

每个阶段

Timers:定时器可以设置回调函数在指定时间后运行。这个回调函数会在能够被调度后,尽可能快的运行。操作系统的调度,或其他回调数的运行,可能会延迟该回调函数的运行。**通常,poll阶段控制定时器的运行。

当事件循环进入poll阶段,根据该阶段队列内容执行:

执行队列中的回调函数

当队列中没有回调函数时,且定时器设置时间已过

回到timers 阶段,执行相应的回调函数

**为了防止poll阶段过长,libuv根据为不同系统设置了poll阶段的最长事件。

I/O callbacks:该阶段执行一些如TCP错误之类的系统操作的会ID回调。

Poll(轮询):

两个主要功能

执行定时器设置并到期的回调函数,然后

处理poll队列中的事件

工作机制:当没有timers被调度,分两种情况

如果poll队列不为空,会挨个执行队列里的callback,直到队列为空,或达到系统限制

如果poll队列为空,分两种情况:

如果执行了setImmediate(),eventloop会结束poll阶段,进入到check阶段执行

如果没有执行setImmediate(),eventloop会等待callback进入队列

一旦poll队列为空,evetloop会检查timers,如果计时已到,event loop 会回到 timers 阶段,执行相应的回调函数.

check阶段

poll阶段变为空闲、等待状态时,一旦调用setImmediate(),eventloop会进入check 阶段,而不是在poll阶段等待。

close callbacks阶段

例如:socket或句柄关闭,close事件会触发这个阶段。或者通过process.nextTick()触发

总结

Node.js事件循环有实际上7、8个阶段,但我们关注-node实际使用的,就是前面说的几个。每个阶段都有自己的队列。本阶段执行完成后,执行下一个阶段。循环顺序不是完全固定的,很多阶段都是由外部事件触发的。

重要的三阶段:

timers,定时器阶段:

执行定时任务(setTimeOut(), setInterval())

poll 轮询阶段:

处理到期的定时器任务,然后(因为最开始阶段队列为空,一旦队列为空,就会检查是否有到期的定时器任务)

处理队列任务,直到队列空,或达到上限

如果队列为空:如果setImmediate,终止轮询阶段,进入检查阶段执行。如果没setImmediate,查看有没有定时器任务到期,有的话就到timers阶段,执行回调函数.

check 检查阶段

轮询阶段空闲,且有setImmediate的时候,进入检查阶段

不重要二阶段

I/O callbacks阶段:处理I/O异常错误

close callbacks阶段: 处理各种close事件回调

代码

关于poll阶段和timers阶段调用先后

const fs = require('fs');

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

function func(cb) {
fs.readFile('xxx.js',cb);
}

func( () => {
console.log('a');
})


参考

https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/#why-would-that-be-allowed
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息