Node.js学习记录
Node 中的 Event Loop 和浏览器中的是完全不相同的东西。Node.js 采用 V8 作为 js 的解析引擎,而 I/O 处理方面使用了自己设计的 libuv,libuv 是一个基于事件驱动的跨平台抽象层,封装了不同操作系统一些底层特性,对外提供统一的 API,事件循环机制也是它里面的实现
轮询机制
通过libuv库实现
uv_run函数分为六个阶段
timers 定时器阶段
计时和执行到点的定时器回调函数pending callbacks
某些系统操作(如TCP错误类型)的回调函数idle.prepare
准备工作- poll 轮询阶段
如果轮询队列不为空,依次同步取出轮询队列中第一个回调函数执行,直到轮询队列为空或达到系统最大限制;
如果轮询队列为空:如果有setImmediate,直接进入下个check阶段; 如果没有就在poll阶段等待,直到轮询队列添加了新的回调函数或者定时器到点就去下个check阶段; - check
执行setImmediate设置的回调函数 - close/callbacks 关闭阶段
执行close事件回调函数
process.nextTick这个函数其实是独立于 Event Loop 之外的,它有一个自己的队列,当每个阶段完成后,如果存在 nextTick 队列,就会清空队列中的所有回调函数,并且优先于其他 microtask 执行。

外部输入数据–>轮询阶段(poll)–>检查阶段(check)–>关闭事件回调阶段(close callback)–>定时器检测阶段(timer)–>I/O 事件回调阶段(I/O callbacks)–>闲置阶段(idle, prepare)
- timers 阶段:这个阶段执行 timer(setTimeout、setInterval)的回调
- I/O callbacks 阶段:处理一些上一轮循环中的少数未执行的 I/O 回调
- idle, prepare 阶段:仅 node 内部使用
- poll 阶段:获取新的 I/O 事件, 适当的条件下 node 将阻塞在这里
- check 阶段:执行 setImmediate() 的回调
- close callbacks 阶段:执行 socket 的 close 事件回调
日常开发中的绝大部分异步任务都是在timers、poll、check`这 3 个阶段处理的。
(1) timer
timers 阶段会执行 setTimeout 和 setInterval 回调,并且是由 poll 阶段控制的。
同样,在 Node 中定时器指定的时间也不是准确时间,只能是尽快执行。
(2) poll
poll 是一个至关重要的阶段,这一阶段中,系统会做两件事情
- 回到 timer 阶段执行回调
- 执行 I/O 回调
并且在进入该阶段时如果没有设定了 timer 的话,会发生以下两件事情
如果 poll 队列不为空,会遍历回调队列并同步执行,直到队列为空或者达到系统限制
如果 poll 队列为空时,会有两件事发生
- 如果有 setImmediate 回调需要执行,poll 阶段会停止并且进入到 check 阶段执行回调
- 如果没有 setImmediate 回调需要执行,会等待回调被加入到队列中并立即执行回调,这里同样会有个超时时间设置防止一直等待下去
当然设定了 timer 的话且 poll 队列为空,则会判断是否有 timer 超时,如果有的话会回到 timer 阶段执行回调。
(3) check 阶段
setImmediate()的回调会被加入 check 队列中,从 event loop 的阶段图可以知道,check 阶段的执行顺序在 poll 阶段之后。
注意
- setImmediate 设计在 poll 阶段完成时执行,即 check 阶段;
- setTimeout 设计在 poll 阶段为空闲时,且设定时间到达后执行,但它在 timer 阶段执行
简单实现Node的Events模块
参考回答:
简介:观察者模式或者说订阅模式,它定义了对象间的一种一对多的关系,让多个观察者对象同时监听某一个主题对象,当一个对象发生改变时,所有依赖于它的对象都将得到通知。
node中的Events模块就是通过观察者模式来实现的:
1 | var events=require('events'); |
这样,eventEmitter发出say事件,通过On接收,并且输出结果,这就是一个订阅模式的实现,下面我们来简单的实现一个Events模块的EventEmitter。
(1)实现简单的Event模块的emit和on方法
1 | function Events(){ |
这样我们就定义了Events,现在我们可以开始来调用:
1 | var events=new Events(); |
//结果就是通过emit调用之后,输出了Jony yu
(2)每个对象是独立的
因为是通过new的方式,每次生成的对象都是不相同的,因此:
1 | var event1=new Events(); |
//event1、event2之间的事件监听互相不影响
//输出结果为’Jony event1’ ‘Jony event2’
https://zhuanlan.zhihu.com/p/54882306
实现Event(event bus)
event bus既是node中各个模块的基石,又是前端组件通信的依赖手段之一,同时涉及了订阅-发布设计模式,是非常重要的基础。
简单版:
1 | class EventEmeitter { |
面试版:
1 | class EventEmeitter { |