乐趣区

关于node.js: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')

二、公布订阅模式

 定义对象间一对多的依赖关系 

模式图解:

因素:

 缓存队列,寄存订阅者信息
具备减少、曹处订阅的能力
状态扭转时告诉所有订阅者执行监听 

模仿实现:

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 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

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');
    })
})
// 程序确定,按队列程序切换执行 
退出移动版