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');// offlet 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')
二、公布订阅模式
定义对象间一对多的依赖关系
模式图解:
因素:
缓存队列,寄存订阅者信息具备减少、曹处订阅的能力状态扭转时告诉所有订阅者执行监听
模仿实现:
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:
事件轮询机制图解:
队列阐明:
timers:执行setTimeout与setInterval回调pedding callbacks:执行零碎操作的回调,例如:tcp udpidle,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
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'); })})// 程序确定,按队列程序切换执行