(Ryan的Koa系列博客)4.依赖库:events概述

前端之家收集整理的这篇文章主要介绍了(Ryan的Koa系列博客)4.依赖库:events概述前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

前言

events库是node的内置库的内容,简单点说就是node的事件发布器。(订阅\发布)
node的事件发布器可以为所有的引擎提供事件发布功能

安装

npm install events

依赖

var EventEmitter = require('events').EventEmitter

node的事件机制

大多数 Node.js 核心 API 都是采用惯用的异步事件驱动架构,其中某些类型的对象(一般称为触发器emitters)会周期性地发送(触发)被命名好的相关事件来调用被称为监听器的函数对象(一般称为触发器listeners)。

例如:
每次对等连接(a peer connects to it)时或者新的连接时,一个服务器(net.Server)对象都会发出(触发)一个事件;
文件打开时,一个文件流(fs.ReadStream)对象都会发出(触发)一个事件;
只要数据可用被读取,一个流(stream )对象都会发出(触发)一个事件。

所有能发送(触发)事件的对象都是EventEmitter类的实例。这些对象都会暴露一个名为eventEmitter.on()的函数,并允许一个或者多个函数通过这个对象与一些有名字的事件关联到一起。事件名通常都是驼峰形式的字符串,但是,也可以用任何有效的js属性键作为事件名。

???
当EventEmitter对象发出一个事件时,所有附加在特定事件上的函数都被同步地调用,被调用的监听器返回的任何值将被忽略和丢弃。(什么叫做:被调用的监听器返回的任何值将被忽略和丢弃?是不是正是因为如此,才需要使用高级函数呢?)
???

以下例子展示了一个只有单个监听器(listener)的 EventEmitter 实例。 eventEmitter.on() 方法用于注册监听器(listener),eventEmitter.emit() 方法用于触发事件。下面我们看一个例子:

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();
console.log('开始注册事件');
myEmitter.on('event',() => { console.log('发生了一个事件'); }); console.log('开始触发事件事件'); myEmitter.emit('event'); 

执行结果:

给监听器(listener)传入参数与this的使用方法

在es5的语法下,eventEmitter.emit() 方法允许将任意参数集合传给监听器函数。 当一个普通的监听器函数被 EventEmitter 调用时,标准的 this 关键词会被设置指向监听器所附加的 EventEmitter。也就是说,this在监听器关联的事件中就代表EventEmitter对象。

我们来看一个例子:

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();
myEmitter.on('event',function(a,b) {
    console.log(a,b,this);
    // Prints:
    // a b MyEmitter {
    // domain: null,
    // _events: { event: [Function] },
    // _eventsCount: 1,
    // _maxListeners: undefined }
});
myEmitter.emit('event','a','b');

执行结果:

也可以使用 ES6 的箭头函数作为监听器。但是这样 this 关键词就不再指向 EventEmitter 实例,我们来看es6的例子:

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();
myEmitter.on('event',(a,b) => {
    console.log(a,this);
// Prints: a b {}
});
myEmitter.emit('event','b');

同步和异步

EventListener 会按照监听器注册的顺序同步地调用所有监听器。 所以需要确保事件的正确排序且避免竞争条件或逻辑错误。 监听器函数可以使用 setImmediate() 或 process.nextTick() 方法切换为异步操作模式。

来我们看个例子:

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();
myEmitter.on('event',b) => {
    setImmediate(() => {
        console.log('这个是异步发生的A');
});
console.log('这个是监听器的B');
// Prints:这个是监听器的B
//这个是异步发生的A
});
myEmitter.emit('event','b');

让事件使用一次之后被销毁

使用 eventEmitter.on() 方法注册监听器时,监听器会在每次触发命名事件时都被调用

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();
var m = 0;
myEmitter.on('event',() => {
    console.log(++m);
});
myEmitter.emit('event');
// 打印: 1
myEmitter.emit('event');
// 打印: 2

如果,不想让某个被注册的事件被调用多次,可以使用eventEmitter.once() 。使用 eventEmitter.once() 可以使被注册的事件,只能被监听器调用一次。 当事件被触发后,该事件的监听器会先被注销,然后再调用事件中的功能函数。我们来看下边的例子:

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();
var m = 0;
myEmitter.once('event',() => {
    console.log(++m);
});
myEmitter.emit('event');
// 打印: 1
myEmitter.emit('event');
// 忽略

报错事件

When an error occurs within an EventEmitter instance,the typical action is for an ‘error’ event to be emitted. These are treated as special cases within Node.js.

If an EventEmitter does not have at least one listener registered for the ‘error’ event,and an ‘error’ event is emitted,the error is thrown,a stack trace is printed,and the Node.js process exits.

当 EventEmitter 实例中发生错误时,典型做法应该是去注册一个 ‘error’ 事件,并触发这个事件。但是,这在 Node.js 中是个特例,或者说不应该被推荐的。因为,如果 EventEmitter 没有为 ‘error’ 事件注册至少一个监听器,则当 ‘error’ 事件触发时,会抛出错误,打印堆栈信息,然后退出 Node.js 进程。我们来看下边的一个例子:

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();
myEmitter.emit('error',new Error('whoops!'));
// Throws and crashes Node.js

如果又不想为事件注册监听器,又想防止 Node.js 进程崩溃,可以在 process 对象的 uncaughtException 事件上注册监听器。(node核心中提供了domain 模块,但是,该模块官方已经准备废弃了,我用过这个domain 模块,该模块的问题是过于占用内存,会影响整个系统的性能。不知道该模块的底层是什么样的结构,反正,是一个已经准备废弃的模块了,因此,也不需要去查看了),我们来看这样一个例子:

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();

process.on('uncaughtException',(err) => {
    console.log('有错误');
});

myEmitter.emit('error',new Error('whoops!'));
// 打印: 有错误

当然,作为最佳实践,应该始终为 ‘error’ 事件注册监听器。

例子如下:

const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();
myEmitter.on('error',(err) => {
    console.log('有错误');
});
myEmitter.emit('error',new Error('whoops!'));
// 打印: 有错误

后记

本文只是依赖库events的概述。通过本文,我们知道了最主要的事件类EventEmitter,也知道了其他的事件类都是EventEmitter的实例,同时,我们还学习了注册、监听、触发、错误处理等多种事件的操作。node的异步事件是它的特殊之一,因此,学好node事件,是学好node的基础。本文篇幅有限,因此,关于EventEmitter类的深入解析将会在后续的文章中进行讲解。

猜你在找的设计模式相关文章