写点什么

NodeJs 深入浅出之旅:异步 I/O (中)🐉

用户头像
空城机
关注
发布于: 29 分钟前
NodeJs深入浅出之旅:异步I/O (中)🐉

监听器

此文是承接上文《NodeJs深入浅出之旅:异步I/O (上)》的,所以对于监听器的介绍可以查看之前的内容,或者去API中查看说明


  • 事件监听器模式是一种广泛用于异步编程的模式,是回调函数的事件化,又称发布/订阅模式

  • Node 自身提供的 events 事件触发器模块是发布/订阅模式的一个简单实现。

  • 所有触发事件的对象都是 EventEmitter 类的实例

监听器只监听一次

可以设置监听器为once(),这样监听就可以只调用一次,不会过多调用。一旦事件被触发,则监听器就会被注销然后被调用。


这种方法对于某些只需要执行一次的查询时效果很明显,比如 SQL 在进行查询时,新到来的相同调用只需在队列中等待数据就绪即可,一旦查询结束,得到的结果可以被这些调用共同使用。这种方式能节省重复的调用产生的开销。 由于 Node 单线程执行的原因,此处也无须担心状态同步问题。

myEmitter.once('event2', (val) => {    console.log(val);});myEmitter.emit('event2', 'hello world!');myEmitter.emit('event2', 'ok');
复制代码

结果:


此监听器方法可以在读取文件数据时减少读取次数


读取方法设置在 IOcode/index.js


在这里定义readTxt读取方法时,设置了Promise,这样时为了之后调用时获取返回值更加简洁方便,如果出现报错或者正确的返回结果也可以直接使用.then获取。 (PS:因为工作不能打包,还要兼容 IE9,所以根本用不了 ES6。 只能自学期间时不时添加一下,也只有平时多练习才能熟练使用,惨😭)

let status = 'ready'myEmitter.once('read', () => {    status = 'peding'})// 使用once,让接口访问数据第一次生效function readTxt(namePath) {    return new Promise((reslove, rejects)=>{        fs.readFile(namePath, function(err, data) {             if (err) {                rejects('错误');            } else {                if (status === 'ready') {                    myEmitter.emit('read');                    reslove(data);                } else {                    rejects('');                }            }        })    })}module.exports = { readTxt }
复制代码


入口函数: 调用时使用requireIOcode/index.js 导入到入口文件中

var IO = express();let IoCode = require('./IOcode')IO.get('/txt', (req, res) => {     let dpromise = IoCode.readTxt('./src/a.txt');    dpromise.then((val)=>{        res.json({ d: val.toString() })    }, ()=> {        res.end();    })})IO.listen('3001',function(){    console.log("启动了。。。")})
复制代码


结果:



监听器 error 事件

<font color=#f00 size=2 >为了处理异常,EventEmitter对象对error事件进行了特殊处理。在Node.js中被视为特殊情况</font>


如果运行期间的错误触发了error事件,EventEmitter回检查是否对error事件添加过监听器。 如果添加了,这个错误将会交由该监听器处理, 否则这个错误将会作为异常抛出。 如果外部没有捕获这个异常,会引起线程退出。


一个健壮的EventEmitter实例应该对error事件做处理。


如果只是发布错误, 异常会抛出,打印堆栈跟踪,然后进程结束

myEmitter.emit('error', new Error('mistake!'));
复制代码


如果设置了error错误监听器,会转入监听器处理错误,然后进程可以走下去

myEmitter.on('error', (err) => {    console.log('this is an error');})myEmitter.emit('error', new Error('mistake!'));
复制代码



移除监听器 removeListener

监听器也可以主动进行移除,移除的命令是removeListener或者removeAllListeners


removeListener移除指定监听器,removeAllListeners移除所有监听器。


参考:http://nodejs.cn/api/events.html#events_event_removelistener


如下面的例子,statusChange方法已经被removeListenermyEmitter监听器当中移除,在emit调用read时,是不会被触发的。

例子:

let status = 'ready';let statusChange = () => {    status = 'peding';    console.log(status);};myEmitter.once('read', statusChange)
myEmitter.removeListener('read', statusChange);myEmitter.emit('read');
复制代码


移除监听器大多数使用的情况是在项目中有很多监听器,其他的某些监听器对你是不需要的,但是冒然删除并不合适,可以使用removeListener的方式来移除特定的监听器。




多异步之间的协作方案

事件发布/订阅模式有其优点。 利用高阶函数侦听器作为回调函数可以随意添加和删除,这能够帮助开发者轻松处理随时可能添加的业务逻辑,也可以隔离业务逻辑,保持业务逻辑单元的职责单一。


一般而言,事件与侦听器是一对多的关系,但是在异步编程时,可能会出现事件与侦听器是多对一的情况。 比如一个业务逻辑依赖多个通过回调或事件传递的结果。


例子: 一个方法A变量a改变需要在方法B和方法C都执行后才能进行 1、定义方法 A

function A(res) {    console.log(`打印结果:`);    console.log(res);    Object.values(res).map((item)=>{        item();    })}
复制代码


2、定义一个高阶函数after,然后定义一个done方法为after的返回函数,将方法 A 传入回调函数当中,设置times次数为 2。


以上的意义在于当监听器监听到done被调用时,可以将传入的keyvalue加入results当中,然后根据results内的属性名称数量是否等于设定的times次数来判断方法A是否执行

let after = function(times, callback) {    let results = {}    return (key, value)=>{        let hasArr = Object.getOwnPropertyNames(results);        if (hasArr.indexOf(key) == -1) {            results[key] = value;            if (hasArr.length == times - 1) {                callback(results);            }        }    }}let done = after(2, A)myEmitter.on('done', done)
复制代码


3、定义需要的先决方法B、C

function B() {    myEmitter.emit('done', 'fun1', ()=> { console.log('fun1'); })}function C() {    myEmitter.emit('done', 'fun2', ()=> { console.log('fun2'); })}
复制代码


然后执行 B 和 C, A 的执行将会在 C 方法调用后才会执行

setTimeout(()=>{    IoCode.C();}, 2000)IoCode.B();
复制代码


结果:


当然了,在多异步之间的协作时,也可以使用EventProxy。 这是一个很轻量的工具,但是能有效的解决高并发大流量的控制。


安装:cnpm install eventproxy 或者 yarn add eventproxy


调用:var EventProxy = require('eventProxy');


参考文章:《eventProxy 解决回调坑 (Node.js)》


示例:EventProxy提供了一个all()方法来订阅多个事件,需要每个事件都被触发后,侦听器才会执行


let ep = new EventProxy();ep.all('temp', 'home', function(temp, home) {    console.log(`${temp}: ${home}`);})
function E() { ep.emit('temp', 10) }function F() { ep.emit('home', 8) }
复制代码


EventProxy在事件发布/订阅模式的基础上还完善了异常处理。 使用fail()方法将可以监听事件中的错误




在异步编程解决方案还有 Promise/Deferred 模式和流程控制库...👻

发布于: 29 分钟前阅读数: 5
用户头像

空城机

关注

曾经沧海难为水,只是当时已惘然 2021.03.22 加入

业余作者,在线水文 主要干前端的活,业余会学学python 欢迎各位关注,互相学习,互相进步

评论

发布
暂无评论
NodeJs深入浅出之旅:异步I/O (中)🐉