node常用内置模块(events)

node常用内置模块(events) 一、事件模块events

通过EventEmitter类实现事件统一管理

常用API:
on:添加事件监听 emit:触发事件 once:添加只触发一次的事件监听 off:移除特定的监听

代码展示:
const EventEmitter = require('events'); const ev = new EventEmitter(); ev.once('start', (...args) => {// 只执行一次 console.log('start once', args); }); ev.on('start', function() { console.log('tart on'); console.log('this',this); })ev.emit('start','message data'); // off let startcb = ()=>{ console.log('jiang') } ev.on('startcb', startcb); ev.off('startcb',startcb); ev.emit('startcb'); // 不会触发const fs = require('fs'); // 许多内置模块都继承了EventEmitter类,可直接使用该类的方法 const crt = fs.createReadStream(); crt.on('data')

二、发布订阅模式
定义对象间一对多的依赖关系

模式图解:
node常用内置模块(events)
文章图片

要素:
缓存队列,存放订阅者信息 具有增加、曹处订阅的能力 状态改变时通知所有订阅者执行监听

模拟实现:
class PubSub { constructor() { this._events = {} } subscribe(event, callback) { if (this._events[event]) { this._events[event].push(callback); } else { this._events[event] = [callback]; } }publish(event, ...args) { const items = this._events[event]; if (items && items.length) { items.forEach(callback => { callback.call(this, ...args); }) } } }let ps = new PubSub(); ps.subscribe('event1',(...args)=>{ console.log('event1 run end',args); })ps.publish('event1','event1 data')

EventEmitter模拟实现:
function MyEvent(){ // 用户缓存订阅者信息 this._events = Object.create(null); // 创建一个空对象,不带任何原型 }MyEvent.prototype.on = function(type,callback){ this._events[type] = this._events[type] || []; this._events[type].push(callback); }MyEvent.prototype.emit = function(type,...args){ if(this._events[type] && this._events[type].length){ this._events[type].forEach(callback=>{ callback.call(this,...args); }) }else{ console.log('没有订阅者'); } }MyEvent.prototype.off = function(type,callback){ // 判断当前 type 事件监听是否存在,如果存在,则取消指定的事件监听 if(this._events && this._events[type]){ this._events[type] = this._events[type].filter(cb=>{ return cb !== callback && cb.link !==callback; }) } }MyEvent.prototype.once = function(type,callback){ let cb = (...args)=>{ callback.call(this,...args); this.off(type,cb); } cb.link = callback; this.on(type,cb); }let ev = new MyEvent(); let cb = function(...args){ console.log(args); } ev.once('event1',cb); ev.off('event1',cb); // 取消监听 ev.emit('event1',1,2); ev.emit('event1',1,2);

三、事件轮询eventloop
基于浏览器的事件轮询eventloop:
从上至下执行所有的同步代码 执行过程中,将遇到的宏任务与微任务依次添加到相应的任务列队中 同步代码执行完成过后,执行满足条件的微任务回调 微任务队列执行完毕之后执行所有满足需求的宏任务回调 执行事件轮询操作 注意:每次执行完一个宏任务之后就会立即检查微任务队列

练习:
setTimeout(() => { console.log('s1'); Promise.resolve().then(() => { console.log('p2'); }) Promise.resolve().then(() => { console.log('p3'); }) })Promise.resolve().then(() => { console.log('p1'); setTimeout(() => { console.log('s2'); }) setTimeout(() => { console.log('s3'); }) })// p1 s1 p2 p3 s2 s3

nodejs下的事件轮询eventloop:
事件轮询机制图解:
node常用内置模块(events)
文章图片

队列说明:
timers:执行setTimeout与setInterval回调 pedding callbacks:执行系统操作的回调,例如:tcp udp idle,prepare:只在系统内部进行使用 poll:执行与I/O相关的回调 check:执行setImmediate中的回调 close callbacks:执行close事件的回调

nodejs完整事件轮询:
执行同步代码,将不同的任务添加至相应的队列 所有同步代码执行完成之后就会执行满足添加的微任务 所有微任务代码执行后会执行timer队列中满足条件的宏任务 timer中的所有宏任务执行完成后(10版本之前是却换任务队列之前)就会依次切换队列 注意:每次执行完一个宏任务之前都会先'清空'微任务代码,nextTick优先级高于promise

练习1:
setTimeout(()=>{ console.log('s1'); })Promise.resolve().then(()=>{ console.log('p1'); })console.log('start'); process.nextTick(()=>{ console.log('nextTick'); }); setImmediate(()=>{ console.log('setImmediate'); })console.log('end'); // start end nextTick p1 s1 setImmediate

练习2:
setTimeout(()=>{ console.log('s1'); Promise.resolve().then(()=>{ console.log('p1'); }) process.nextTick(()=>{ console.log('t1'); }) }) Promise.resolve().then(()=>{ console.log('p2'); }) console.log('start'); setTimeout(()=>{ console.log('s2'); Promise.resolve().then(()=>{ console.log('p3'); }) process.nextTick(()=>{ console.log('t2'); }) }); console.log('end'); //start end p2 s1 t1 p1 s2 t2 p3

nodejs与浏览器事件轮询的区别
任务队列数不同 每次执行完一个宏任务都会去清空微任务 nodejs中process.nextTick优先于promise.then

【node常用内置模块(events)】nodejs事件轮询的常见问题:
// setTimeout(()=>{ //console.log('timeout'); // })// 虽然默认是0,但是cpu的时间片不确定,所以可能在setImmediate之后入队执行 // setImmediate(()=>{ //console.log('immediate'); // }) // 执行顺序不确定const fs = require('fs'); fs.readFile('./m1.js',()=>{ setTimeout(()=>{ console.log('timeout'); },0); setImmediate(()=>{ console.log('immediate'); }) }) // 顺序确定,按队列顺序切换执行

    推荐阅读