Emitter 学习笔记
Emitter 学习笔记
学习感想
最好的学习资料是官方文档, 由于是英语的, 可能造成心里压力, 阅读不下去(我就是).
我个人是先从一篇 emitter 的博客对 Emitter 稍有了解再去看官方文档的, 果然这个模块真是小还简单.
如果这篇文章有幸被你参考, 倍感荣幸!
概要介绍
中文翻译 不明所以, 大概是事件分发的意思.
- emit: 散发,发射
- emitter: 发射器
Nodejs 中很多 API 或者内部类是基于异步调用的事件机制的, 在 nodejs 中,所有能 emit 事件的类都继承自 Emitter
Emitter 是 events模块导出的唯一的类, 通常作为接口被其他类继承(或可以直接实例化使用,写 demo 的时候可以这么做)
一个继承了 Emitter 的类, 就被注册事件, 也有了响应事件调用回调函数的能力
当 Emitter 类的某个事件被触发了, 这个事件绑定的所有回调函数, 将会按照 绑定顺序 同步执行.
通过 demo 一步一步了解 Emitter
注意: 所有的 demo 都是 ES6 的写法
demo1: API - on(绑定事件) & emit(触发事件)
通过 Emitter.on()方法绑定事件, 绑定的回调方法, 可以是匿名函数, 也可以是 ES6 的箭头函数
注意: Emitter.on() 方法有个别名, 即 Emitter.addListener() 方法
emitter.on(eventName, listener)
通过 Emitter.emit()方法触发事件,可以传参数的
emitter.emit(eventName[, ...args])
下面是一个可以直接执行的 demo
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
//myEmitter.addListener(‘event', () => {
console.log('an event occurred!');
});
myEmitter.emit('event');
输出结果:
an event occurred!
demo2: 通过 emit 向事件回调方法传入参数
通过 on() 绑定的回调方法是可以带有参数的,这些参数通过 emit() 方法传进回调函数中
下面是一个可以直接执行的 demo
const EventEmitter = require('events');
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', function(a, b) {
console.log(a, b, this);
});
myEmitter.emit('event', 'a', 'b');
输出结果:
a b MyEmitter {
domain: null,
_events: { event: [Function] },
_eventsCount: 1,
_maxListeners: undefined }
demo3: 事件回调方法的执行顺序
一个事件可以绑定多个回调方法, 当这个事件被触发时, 所有的回调方法会按照它们绑定的顺序依次同步执行.
注意: 回调事件中可以执行异步操作
下面是一个可以直接执行的 demo
const EventsEmitter = require('events');
class MyEmitter extends EventsEmitter {}
let myEmitter = new MyEmitter();
const cb1 = () => setTimeout(() => {
console.log('cb1')
}, 100);
// const cb1 = () => console.log('cb1');
const cb2 = () => console.log('cb2');
const cb3 = () => console.log('cb3');
const cb4 = () => console.log('cb4');
myEmitter.on('me',cb1);
myEmitter.on('me',cb2);
myEmitter.on('me',cb3);
myEmitter.on('me',cb4);
myEmitter.emit('me');
输出结果:
cb2
cb3
cb4
cb1
demo4: API - once(只会被调用一次的回调方法)
通过 on() 绑定的回调方法, 无论被 emit() 触发多少次, 都会被执行.
通过 once() 绑定的回调方法, 在回调方法绑定后第一次触发才会被执行.(实际上执行后该回调方法, 就在该事件的回调列表中去除了).
注意: once() 指的是该回调方法只会被触发一次, 并非该事件只会被触发一次
下面是一个可以直接执行的 demo
const EventsEmitter = require('events');
class MyEmitter extends EventsEmitter {}
let myEmitter = new MyEmitter();
const cb1 = () => console.log('cb1');
const cb2 = () => console.log('cb2');
const cb3 = () => console.log('cb3');
myEmitter.once('me',cb1);
myEmitter.once('me',cb2);
myEmitter.on('me',cb3);
// 执行两次 me 事件
myEmitter.emit('me');
myEmitter.emit('me');
输出结果:
cb1
cb2
cb3
cb3
demo5: API - prependListener(在队首添加回调方法)
绑定事件的方法,用法同 on().
然而通过 on 绑定的回调方法, 会 push 进回调方法队列, 后绑定后执行.
通过 prependListener 绑定的回调方法, 则是会 unshift 进回调方法队列, 后绑定先执行.
下面是一个可以直接执行的 demo
const EventsEmitter = require('events');
class MyEmitter extends EventsEmitter {
}
let myEmitter = new MyEmitter();
const cb1 = () => console.log('cb1');
const cb2 = () => console.log('cb2');
const cb3 = () => console.log('cb3');
const cb4 = () => console.log('cb4');
myEmitter.on('me', cb1);
myEmitter.on('me', cb2);
myEmitter.on('me', cb3);
myEmitter.prependListener('me', cb4);
myEmitter.emit('me');
输出结果:
cb4
cb1
cb2
cb3
demo6: API - prependOnceListener(队首绑定一次性回调)
不赘述, 参考 once() & prependListener()
没有什么特殊的
demo7: API - setMaxListeners & getMaxListeners (get & set 可绑定回调的上限)
默认情况下,每个事件的可绑定回调方法上限是10个, 当绑定第11个回调方法的时候, 程序会抛出 warning.虽然程序会抛出 warning, 但是当事件被触发的时候, 所有的回调方法都会正常执行.
setMaxListeners 方法是设置 Emitter 可绑定回调方法的上限(不区分每一个事件的可绑定回调方法上限, 可增可减).
getMaxListeners 方法则是获取某个 Emitter 的可绑定回调方法上限.
下面是一个可以直接执行的 demo
const EventsEmitter = require('events');
class MyEmitter extends EventsEmitter {
}
let myEmitter = new MyEmitter();
// 没有下面这一行, 程序会包 warning 哟
// myEmitter.setMaxListeners(20);
const cb1 = () => console.log('cb1');
const cb2 = () => console.log('cb2');
const cb3 = () => console.log('cb3');
const cb4 = () => console.log('cb4');
const cb5 = () => console.log('cb5');
const cb6 = () => console.log('cb6');
const cb7 = () => console.log('cb7');
const cb8 = () => console.log('cb8');
const cb9 = () => console.log('cb9');
const cb10 = () => console.log('cb10');
const cb11 = () => console.log('cb11');
const cb12 = () => console.log('cb12');
const cb13 = () => console.log('cb13');
const cb14 = () => console.log('cb14');
const cb15 = () => console.log('cb15');
myEmitter.on('me', cb1);
myEmitter.on('me', cb2);
myEmitter.on('me', cb3);
myEmitter.on('me', cb4);
myEmitter.on('me', cb5);
myEmitter.on('me', cb6);
myEmitter.on('me', cb7);
myEmitter.on('me', cb8);
myEmitter.on('me', cb9);
myEmitter.on('me', cb10);
myEmitter.on('me', cb11);
myEmitter.on('me', cb12);
myEmitter.on('me', cb13);
myEmitter.on('me', cb14);
myEmitter.on('me', cb15);
myEmitter.emit('me');
console.log('max listeners = ' + myEmitter.getMaxListeners());
输出结果:
cb1
cb2
cb3
cb4
cb5
cb6
cb7
cb8
cb9
cb10
cb11
cb12
cb13
cb14
cb15
max listeners = 10
(node:14318) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 me listeners added. Use emitter.setMaxListeners() to increase limit
demo8: API - listenerCount(某个事件回调列表的 length)
这个方法返回某个事件所绑定的 回调方法的个数.右有两种写法, 结果是一致的.(这些 api 就是这么简单)
下面是一个可以直接执行的 demo
const EventsEmitter = require('events');
class MyEmitter extends EventsEmitter {}
let myEmitter = new MyEmitter();
myEmitter.on('event', () => {});
myEmitter.on('event', () => {});
console.log(EventsEmitter.listenerCount(myEmitter, 'event'));
console.log(myEmitter.listenerCount('event'));
输出结果:
2
2
demo9: API - listeners(返回事件回调方法列表)
获取某个事件的所有回调方法的列表, 顺序是回调方法的执行顺序.
注: 想看到结果, 要把 listeners() 的返回值打印一下
下面是一个可以直接执行的 demo
const EventsEmitter = require('events');
class MyEmitter extends EventsEmitter {
}
let myEmitter = new MyEmitter();
const cb1 = () => console.log('cb1');
const cb2 = () => console.log('cb2');
const cb3 = () => console.log('cb3');
const cb4 = () => console.log('cb4');
myEmitter.on('me', cb1);
myEmitter.on('me', cb2);
myEmitter.on('me', cb3);
myEmitter.on('me', cb4);
console.log('listeners list = ');
console.log(myEmitter.listeners('me'));
输出结果:
listeners list =
[ [Function: cb1],
[Function: cb2],
[Function: cb3],
[Function: cb4]]
demo10: removeAllListeners(移除全部事件回调)
无参数->移除所有的事件
有参数(eventName)->移除某一个事件(eventName)的所有回调方法
下面是一个可以直接执行的 demo
const EventsEmitter = require('events');
class MyEmitter extends EventsEmitter {
}
let myEmitter = new MyEmitter();
const cb1 = () => console.log('cb1');
const cb2 = () => console.log('cb2');
const cb3 = () => console.log('cb3');
myEmitter.on('me', cb1);
myEmitter.on('me', cb2);
myEmitter.on('my', cb3);
console.log('nothing removed:')
myEmitter.emit('me');
myEmitter.emit('my');
console.log('remove me:');
myEmitter.removeAllListeners('me');
myEmitter.emit('me');
myEmitter.emit('my');
console.log('remove all:');
myEmitter.removeAllListeners();
myEmitter.emit('me');
myEmitter.emit('my');
输出结果:
nothing removed:
cb1
cb2
cb3
remove me:
cb3
remove all:
demo11: API - removeListener(移除单个事件回调)
移除某一个事件的某一个回调方法,入参 [eventName,callback] 一个都不能少, 否则报错
如果某个事件,绑定了3个相同的回调方法 cb3,那么 removeListener 只会移除最后绑定的那个 cb3
下面是一个可以直接执行的 demo
const EventsEmitter = require('events');
class MyEmitter extends EventsEmitter {
}
let myEmitter = new MyEmitter();
const cb1 = () => console.log('cb1');
const cb2 = () => console.log('cb2');
const cb3 = () => console.log('cb3');
myEmitter.on('me', cb1);
myEmitter.on('me', cb2);
myEmitter.on('me', cb3);
myEmitter.on('me', cb2);
myEmitter.on('me', cb3);
console.log('before remove:');
myEmitter.emit('me');
console.log('after remove cb2:');
myEmitter.removeListener('me',cb2);
myEmitter.emit('me');
输出结果:
before remove:
cb1
cb2
cb3
cb2
cb3
after remove cb2:
cb1
cb2
cb3
cb3
demo12: 特殊事件 - error 事件
Emitter 支持一个特殊的事件 -error, 当这个事件被触发的时候, 如果找不到处理的回调方法, 会导致程序崩溃.
下面是一个可以直接执行的 demo
const EventsEmitter = require('events');
class MyEmitter extends EventsEmitter {}
let myEmitter = new MyEmitter();
myEmitter.emit('error', new Error('whoops!'));
输出结果:
程序非正常退出
events.js:163
throw er; // Unhandled 'error' event
^
Error: whoops!
at Object.<anonymous> (/Users/zhangtingcen/Documents/my-repo/learning/index.js:60:25)
at Module._compile (module.js:571:32)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:488:32)
at tryModuleLoad (module.js:447:12)
at Function.Module._load (module.js:439:3)
at Module.runMain (module.js:605:10)
at run (bootstrap_node.js:423:7)
at startup (bootstrap_node.js:147:9)
at bootstrap_node.js:538:3
demo13: 特殊事件 - process.uncaughtException
因此我们需要一种手段, 来防止(上面)因为没有正确处理 error 事件导致程序直接崩溃.
可以通过给 precess 绑定 uncaughtException 事件, 当 Emitter 类的 error 事件被触发且没有 error 事件的回调方法时, process 的 uncaughtException 事件回调将会被触发.程序也不会崩溃.
下面是一个可以直接执行的 demo
const EventsEmitter = require('events');
class MyEmitter extends EventsEmitter {}
let myEmitter = new MyEmitter();
process.on('uncaughtException', (err) => {
console.error('whoops! there was an error');
});
myEmitter.emit('error', new Error('whoops!'));
输出结果:
程序正常退出
whoops! there was an error
demo14: 特殊事件 - newListener
newListener 事件回调方法, 会默认传入两个参数[eventName(事件名称),listener(回调方法)
在 newListener 事件回调方法中进行绑定事件的操作是一件危险的事情(分分钟造成死循环).如果确实要在 newListener 的回调方法中绑定新事件, 用 once 绑定这个危险的回调方法.
下面是一个可以直接执行的 demo
const EventsEmitter = require('events');
class MyEmitter extends EventsEmitter {}
let myEmitter = new MyEmitter();
let times = 0;
myEmitter.on('newListener',(event, listener) =>{
//myEmitter.once('newListener',(event, listener) =>{
console.log('running times:' + times++);
console.log(event);
console.log(listener);
myEmitter.on('me',()=>{})
});
myEmitter.on('me',()=>console.log('start running newListener:'));
myEmitter.emit('me');
输出结果:
程序正常退出
程序陷入死循环,执行了不到一秒就崩溃了
demo15: 特殊事件 - removeListener
这个特殊事件已经(我学习的时候20171021)变成 stability:0 - deprecated;,就是不靠谱的, 移除已经在计划中
就不考虑这个了
This blog is under a CC BY-NC-SA 3.0 Unported License
本文链接:http://teeeemoji.xyz/2017/10/21/learn-emitter/