当前位置: 首页 > 后端技术 > Node.js

深入nodejs-core模块Events详解(事件驱动)

时间:2023-04-03 16:40:25 Node.js

事件驱动Node.js是一个基于ChromeV8引擎的JavaScript运行环境。Node.js使用事件驱动、非阻塞I/O模型,使其轻量且高效。允许您在服务器端使用JavaScript构建可扩展的网络应用程序。您应该已经阅读了上面的官方参考资料。Nodejs被设计为基于事件驱动和非阻塞I/O运行。作为事件驱动实现的核心模块Events成为了深入学习node.js的关键。node中大部分模块的实现都继承了Events类。比如文件操作中的fs事件流,网络编程中使用的tcp和http模块等。回想一下自己写的程序,你会发现很多操作都是基于事件驱动的,Events类。那么问题来了,什么是事件驱动?简单的说就是通过监听事件的状态变化来进行相应的操作。比如读取文件时,文件读取完整,或者文件读取错误,则触发相应的状态,然后调用相应的回调函数进行处理。让我们简单地看几段代码来回忆一下:constfs=require('fs');让rs=fs.createReadStream('1.txt');//监听文件打开操作rs.on('open',function(){console.log('open');});//监听数据流读取rs.on('data',function(data){console.log(data);});//监控错误rs.on('error',function(){console.log('error');});//监听读结束操作rs.on('end',function(){console.log('end');});//监听文件关闭操作rs.on('close',function(){console.log('close');});上面这段是针对文件在创建文件读取流的操作中监听了open、data、error、end、close等几个状态进行了回调处理,同样适用于我们上面的定义通过监听来做出相应的操作事件的状态变化。那么这些监控事件是如何触发的呢?它通过Events类中的emit方法发出事件。那么我们就来看看Events是如何实现这样的监听操作的。Events类主要核心API有on(eventName,listener)和emitter.addListener(eventName,listener):将事件处理函数绑定到指定的事件上once(eventName,listener):指定一个事件处理函数,只执行一次onceforthespecifiedeventemit(eventName[,...args]):triggerthespecifiedeventremoveListener(eventName,listener):removespecifiedeventeventhandlerremoveAllListeners([event]):联系指定事件的所有事件处理程序setMaxListeners设置最大队列让我们看看下面的代码,看看它是如何使用的constevents=require('events');constEventsEmitter=newevents();//===============事件监听部分================EventsEmitter.on('open',function(){console.日志(“打开”);});EventsEmitter.on('data',function(data){console.log(data);});EventsEmitter.on('error',function(){console.log('error');});EventsEmitter.on('end',function(){console.log('end');});EventsEmitter.on('close',function(){console.log('close');});//=============事件触发部分===================//触发打开事件EventsEmitter.emit('open');//触发数据事件,并传递一个字符串参数'test'EventsEmitter.emit('数据','测试');//触发错误事件EventsEmitter.emit('error');//触发结束事件EventsEmitter.emit('end');//触发关闭事件EventsEmitter.emit('close');看完上面的代码,是不是有了更进一步的理解呢?让我们回顾一下文件流监控的顶级代码。实际上,文件在不同的状态下会发出相应的emit事件。在那段代码中,我们没有引入node提供的events模块,因为events模块是在文件流中继承的,所以rs变量也有对应的rs.on()方法。看到这里,我想我应该差不多明白了。那么让我们尝试实现Events类来加深理解。初始化Events模块创建一个Events类初始化this.events保存我们需要监听的事件导出模块classEvents{constructor(){this.events={};}}module.exports=事件;实现Events.on方法on该方法接收两个参数:type:监听事件的类型:回调函数先将对应的事件存储在一个对象中,有两种情况:事件对象不存在,再使用typeasthekeyand[listener]asthevalue在初始化的this.events对象中(注意这里存放的是一个数组,比如数据事件,this.events={data:[callback]}),如果事件已经存在,直推监听功能就这么简单现在,接下来就是等待emit触发了。/***事件监听器*@param{*}type要监听的事件类型*@param{*}监听器回调函数*/on(type,listener){if(this.events[type]){this.events[类型].push(监听器);}else{this.events[type]=[listener];}}这里补充一下,同一个监听事件可以添加多个,所以这里this.events[type]会存储一个数组实现Events.emit方法接收两个参数:type:要事件的类型triggered...rest:几个参数,通过type传递给对应事件的回调函数,在this.events中找到对应的事件事件,这里我们已经存为数组,对应事件的回调。遍历数组并为所有相应的事件执行回调。/***事件触发*@param{*}type要触发的事件类型*@param{...any}rest接收到的几个参数,这个参数会作为参数传递给对应事件的回调函数*/emit(type,...rest){if(this.events[type]){this.events[type].forEach(listener=>{listener.apply(this,rest);});}}写到这里,我们之前的代码可以引入我们写的Events模块实现Events.removeListener方法。这个方法是用来删除事件对应的一个监听函数,所以我们只需要从this.events[type]中删除事件即可删除接收两个参数:type:事件类型listener:要删除的监听函数find通过this.events[type]对应的事件监听函数数组,通过filter过滤要删除的监听函数/***delete指定事件中的监听函数*@param{*}type对应的事件*@param{*}listener要删除的监听函数*/removeListener(type,listener){if(this.events[type]){this.events[type].filter(l=>l!==listener);}}实现Events.once方法这个方法和on是一样的,唯一不同的是只会执行一次,即使多次调用emit触发同一个事件监听器,也只会执行一次。实现方式其实就是对on()方法做一层封装,给on()方法传递一个封装好的wrapper而不是listener。wrapper会在wrapper内部执行一个监听回调函数,然后调用this.removeListern删除回调。/***事件监听,但只执行一次*@param{*}type要监听的事件类型*@param{*}监听回调函数*/once(type,listener){constwrapper=(...rest)=>{listener.apply(this,rest);this.removeListener(类型,包装器);};this.on(类型,包装器);}完整代码classEvents{constructor(){this.events={};}/***事件监听器*@param{*}type监听事件类型*@param{*}监听器回调函数*/on(type,listener){if(this.events[type]){this.events[type].push(监听器);}else{this.events[type]=[listener];}}/***事件监听,但只执行一次*@param{*}type要监听的事件类型*@param{*}监听回调函数*/once(type,listener){constwrapper=(...rest)=>{listener.apply(this,rest);this.removeListener(类型,包装器);};this.on(类型,包装器);}/***事件触发*@param{*}type要触发的事件类型*@param{...any}rest接收到的几个参数,这个参数会作为参数传递给对应事件的回调中函数*/emit(type,...rest){if(this.events[type]){this.events[type].forEach(listener=>{listener.apply(this,rest);});}}/***删除指定事件中的监听函数*@param{*}类型对应的事件*@param{*}listener监听函数被移除*/removeListener(type,listener){if(this.events[type]){this.events[type].filter(l=>l!==listener);}}}module.exports=事件;综上所述,Events模块是node.js的一个非常核心的模块。对你进一步了解node很有帮助。上面只写了几个方法。还有几个API和一些很细的地方可以自己去尝试扩展。这里就不一一写了。如果有写得不好或者看不懂的地方可以给我留言。如果觉得文笔还行,方便的话,请点个赞,谢谢。以下是我新的个人微信公众号公众号,我也会在里面为大家提供原创文章。欢迎您关注。等粉丝够了,我会在里面推出一些视频教程。