事件驱动程序
Node.js 使用事件驱动模型,当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。
当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。
这个模型非常高效可扩展性非常强,因为 webserver 一直接受请求而不等待任何读写操作。(这也称之为非阻塞式IO或者事件驱动IO)
在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。
整个事件驱动的流程就是这么实现的,非常简洁。有点类似于观察者模式,事件相当于一个主题(Subject),而所有注册到这个事件上的处理函数相当于观察者(Observer)。
EventEmitter触发器
大多数 Node.js 核心 API 构建于惯用的异步事件驱动架构,其中某些类型的对象(又称触发器,Emitter)会触发命名事件来调用函数(又称监听器,Listener)。
例如 net.Server 会在每次有新连接时触发事件 fs.ReadStream会在打开文件时触发事件,stream会在数据可读时触发事件。
所有能触发事件的对象都是 EventEmitter
类的实例。 这些对象有一个 eventEmitter.on()
函数,用于将一个或多个函数绑定到命名事件上。。
当 EventEmitter
对象触发一个事件时,所有绑定在该事件上的函数都会被同步地调用。 被调用的监听器返回的任何值都将会被忽略并丢弃。
一个简单的 EventEmitter
实例,绑定了一个监听器。 eventEmitter.on()
用于注册监听器, eventEmitter.emit()
用于触发事件。
Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:
const events = require('events');const myEmitter = new events.EventEmitter();
myEmitter.on('event', () => {console.log('触发事件');
});
myEmitter.emit('event');
将参数和 this 传给监听器
eventEmitter.emit()
方法可以传任意数量的参数到监听器函数。 当监听器函数被调用时, this
关键词会被指向监听器所绑定的 EventEmitter
实例。
const events = require('events');const myEmitter = new events.EventEmitter();
myEmitter.on('event', function(a, b) {console.log(a, b, this, this === myEmitter);
});
myEmitter.emit('event', 'a', 'b');
也可以使用 ES6 的箭头函数作为监听器。但 this
关键词不会指向 EventEmitter
实例:
const events = require('events');const myEmitter = new events.EventEmitter();
myEmitter.on('event', (a, b) => {console.log(a, b, this, this ,this===myEmitter);
});
myEmitter.emit('event', 'a', 'b');
结果输出:
a b {} {} false
异步 VS 同步
EventEmitter
以注册的顺序同步地调用所有监听器。 这样可以确保事件的正确排序,并有助于避免竞态条件和逻辑错误。 当适当时,监听器函数可以使用 setImmediate()
和 process.nextTick()
方法切换到异步的操作模式:
const events = require('events');const myEmitter = new events.EventEmitter();
myEmitter.on('event', (a, b) => {setImmediate(() => {console.log('异步地发生');});
});
myEmitter.emit('event', 'a', 'b');
仅处理事件一次
当使用 eventEmitter.on()
注册监听器时,监听器会在每次触发命名事件时被调用。
const events = require('events');const myEmitter = new events.EventEmitter();
let m = 0;
myEmitter.on('event', () => {console.log(++m);
});
myEmitter.emit('event');
// 打印: 1
myEmitter.emit('event');
// 打印: 2
使用 eventEmitter.once()
可以注册最多可调用一次的监听器。 当事件被触发时,监听器会被注销,然后再调用。
const events = require('events');const myEmitter = new events.EventEmitter();
let m = 0;
myEmitter.once('event', () => {console.log(++m);
});
myEmitter.emit('event');
// 打印: 1
myEmitter.emit('event');
// 不触发
错误事件
当 EventEmitter
实例出错时,应该触发 'error'
事件。 这些在 Node.js 中被视为特殊情况。
如果没有为 'error'
事件注册监听器,则当 'error'
事件触发时,会抛出错误、打印堆栈跟踪、并退出 Node.js 进程。
const events = require('events');const myEmitter = new events.EventEmitter();
myEmitter.emit('error', new Error('错误信息'));
// 抛出错误并使 Node.js 崩溃。
为了防止崩溃 Node.js 进程,通过使用符号 errorMonitor
安装监听器,可以监视 'error'
事件但不消耗触发的错误。
const events = require('events');const myEmitter = new events.EventEmitter();
myEmitter.on(EventEmitter.errorMonitor, (err) => {MyMonitoringTool.log(err);
});
myEmitter.emit('error', new Error('错误'));
// 仍然抛出错误并使 Node.js 崩溃。