参考:
简介
Node.js 是单进程单线程应用程序,那么它是如何实现异步调用的呢?
它维护了六个 FIFO 队列,分别表示不同的功能的函数:
|
|
然后它的单线程只执行一个 while 循环,一直跑这六个的头部任务,直到队列为空或者执行时间到达上限。
每个阶段执行的功能如下:
timer
:这个阶段执行通过setTimeout()
和setInterval()
设置的回调函数;I/O callback
:执行延迟到下一个循环迭代的I/O
回调;idle,prepare
:系统调用,也就是liuv
调用;poll
:轮询阶段,检测新的I/O
事件,执行与I/O
相关的回调(几乎所有的回调都是关闭回调,定时器调度的回调,以及setImmaditate()
),node会在此阶段适当的阻塞check
:此阶段调用setImmadiate()
设置的回调close callbacks
:一些关闭回调,比如说socket.on('close',...)
在每个队列阶段处理完之后,并不是直接进入下一阶段,而是会处理一些中间队列:
- 有 4 个主要类型的队列,被原生的
libuv
事件循环处理。- 过期计时器和间隔队列(Expired timers and intervals Queue):实际是最小堆存储;
- IO 事件队列(IO Events Queue):完成的 I/O 事件;
- 立即执行的队列(Immediate Queue):使用 setImmediate 函数添加的回调;
- 关闭操作队列(Close Handlers Queue):任何一个 close 事件处理器。
- 除了四个主要的队列,这里另外有两个被 Node 上层处理的队列:
- 下一个运转队列(Next Ticks Queue):使用 process.nextTick() 函数添加的回调
- 其他的微队列(other Microtasks Queue):包含其他的微队列如成功的 Promise 回调
事件循环给了 nodejs 这些特性:
Node.js 几乎每一个 API 都是支持回调函数的。
这些接口可以处理大量的并发,所以性能非常高。
Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现。
事件驱动程序
Node.js 使用事件驱动模型,当 web server 接收到请求,就把它关闭然后进行处理,然后去服务下一个 web 请求。当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。
这个模型非常高效可扩展性非常强,因为 webserver 一直接受请求而不等待任何读写操作。(这也称之为非阻塞式 IO 或者事件驱动 IO)
在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。
整个事件驱动的流程就是这么实现的,非常简洁。有点类似于观察者模式,事件相当于一个主题 (Subject),而所有注册到这个事件上的处理函数相当于观察者 (Observer)。
Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:
|
|
以下程序绑定事件处理程序:
|
|
我们可以通过程序触发事件:
|
|