web前端之了解node四:事件循环、EventEmitter

来源:互联网 发布:mac给iphone充电闪跳 编辑:程序博客网 时间:2024/06/15 10:38

web前端之了解node四:事件循环、EventEmitter

Node.js 事件循环

Node.js 是单进程单线程应用程序,但是通过事件和回调支持并发,所以性能非常高。

Node.js 的每一个 API 都是异步的,并作为一个独立线程运行,使用异步函数调用,并处理并发。

Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现。

Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数.

事件驱动程序

Node.js 使用事件驱动模型,当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。

当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。

这个模型非常高效可扩展性非常强,因为webserver一直接受请求而不等待任何读写操作。(这也被称之为非阻塞式IO或者事件驱动IO)

在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。
这里写图片描述

整个事件驱动的流程就是这么实现的,非常简洁。有点类似于观察者模式,事件相当于一个主题(Subject),而所有注册到这个事件上的处理函数相当于观察者(Observer)。

Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:

//引入events模块var events=require('events');//创建eventEmitter对象var eventEmitter=new events.EventEmitter();//创建事件处理程序var connectHandler=function connected(){    console.log('连接成功');    //触发data_received事件    eventEmitter.emit('data_received');}//绑定connection事件处理程序eventEmitter.on('connection',connectHandler);//使用匿名函数绑定data_received事件eventEmitter.on('data_received',function(){    console.log("数据接收成功");});//触发connection事件eventEmitter.emit('connection');console.log('程序执行完毕');

执行结果:

这里写图片描述

Node 应用程序是如何工作的?

在 Node 应用程序中,执行异步操作的函数将回调函数作为最后一个参数, 回调函数接收错误对象作为第一个参数。

接下来让我们来重新看下前面的实例,创建一个1.txt文件。

var fs=require("fs");fs.readFile('E:/study/node/123.txt',function(err,data){    if(err){        console.log(err.stack);        return;    }    console.log(data.toString());});console.log("程序执行完毕");

以上程序中 fs.readFile() 是异步函数用于读取文件。 如果在读取文件过程中发生错误,错误 err 对象就会输出错误信息。

如果没发生错误,readFile 跳过 err 对象的输出,文件内容就通过回调函数输出。

执行结果:

这里写图片描述

EventEmitter

Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。

Node.js里面的许多对象都会分发事件:一个net.Server对象会在每次有新连接时分发一个事件, 一个fs.readStream对象会在文件被打开的时候发出一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。

EventEmitter 类

events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就是事件触发与事件监听器功能的封装。

你可以通过require(“events”);来访问该模块。

//引入events模块var events=require('events');//创建eventEmitter对象var eventEmitter=new events.EventEmitter();

EventEmitter对象如果在实例化时发生错误,会触发 ‘error’ 事件。当添加新的监听器时,’newListener’ 事件会触发,当监听器被移除时,’removeListener’ 事件被触发。

下面我们用一个简单的例子说明 EventEmitter 的用法:

var EventEmitter=require('events').EventEmitter;var event=new EventEmitter();event.on('some_event',function(){    console.log('some_event事件被触发');});setTimeout(function(){    event.emit("some_event");},1000);

运行这段代码,1 秒后控制台输出了 some_event 事件触发。其原理是 event 对象注册了事件 some_event 的一个监听器,然后我们通过 setTimeout 在 1000 毫秒以后向 event 对象发送事件 some_event,此时会调用some_event 的监听器。

EventEmitter的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter支持 若干个事件监听器。

当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递:

var events=require('events');var emitter=new events.EventEmitter();emitter.on('someEvent',function(arg1,arg2){    console.log('listener1',arg1,arg2);}); emitter.on('someEvent',function(arg1,arg2){    console.log('listener2',arg1,arg2);});emitter.emit('someEvent','arg1参数','arg2参数');

运行结果:

这里写图片描述

这里写图片描述
这里写图片描述
这里写图片描述

demo:

以下实例通过 connection(连接)事件演示了 EventEmitter 类的应用:

var events=require('events');var eventEmitter=new events.EventEmitter();// 监听器 #1var listener1=function listener1(){    console.log("监听器listener1执行");}// 监听器 #2var listener2=function listener2(){    console.log("监听器listener2执行");}// 绑定connection事件,处理函数为listener1eventEmitter.addListener('connection',listener1);//绑定connection事件,处理函数为listener2eventEmitter.on('connection',listener2);var eventListeners=require('events').EventEmitter.listenerCount(eventEmitter,'connection');console.log(eventListeners+"个监听器监听连接事件");//处理connection事件eventEmitter.emit('connection');//移除监听器绑定的listener1函数eventEmitter.removeListener('connection',listener1);console.log('listener1不再监听');//触发连接事件eventEmitter.emit('connection');eventListeners=require('events').EventEmitter.listenerCount(eventEmitter,'connection');console.log(eventListeners+"个监听器监听连接事件");console.log('程序执行完毕');

这里写图片描述

error 事件

EventEmitter 定义了一个特殊的事件 error,它包含了错误的语义,我们在遇到 异常的时候通常会触发 error 事件。

当 error 被触发时,EventEmitter 规定如果没有响 应的监听器,Node.js 会把它当作异常,退出程序并输出错误信息。

我们一般要为会触发 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。例如:

var events=require('events');var emitter=new events.EventEmitter();emitter.emit('error');

这里写图片描述

继承 EventEmitter

大多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。

为什么要这样做呢?原因有两点:

首先,具有某个实体功能的对象实现事件符合语义, 事件的监听和发射应该是一个对象的方法。

其次 JavaScript 的对象机制是基于原型的,支持 部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系。

1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 7天小狗流鼻涕怎么办 上火鼻涕有血丝怎么办 孕晚期感冒流鼻涕怎么办 孕晚期流鼻涕打喷嚏怎么办 孕晚期鼻塞流鼻涕怎么办 孕晚期流鼻涕怎么办呢 鼻子干燥有血痂怎么办 九个月宝宝鼻炎怎么办 两岁宝贝流鼻涕怎么办 咳嗽流清鼻涕怎么办 鼻塞咳嗽痰多怎么办 宝宝鼻涕流不止怎么办 咳嗽有痰鼻塞怎么办 舌头长水泡怎么办儿童 小孩淌清鼻涕怎么办 小孩淌清水鼻涕怎么办 闻到辣的流鼻涕怎么办 舌头上长囊肿怎么办 单侧鼻塞流清涕怎么办 鼻塞流清鼻涕怎么办 睡眠脑子总幻想怎么办 鼻塞流清水鼻涕怎么办 坐月子流清鼻涕怎么办 六岁宝宝牙疼怎么办 哺乳期感冒鼻塞流鼻涕怎么办 婴儿晚上睡觉晚怎么办 一个月孩子缺钙怎么办 七八岁睡眠不好怎么办 小孩溜黄鼻涕怎么办 孕妇鼻塞黄鼻涕怎么办 孕妇上火黄鼻涕怎么办 孕妇鼻涕很黄怎么办 孕妇鼻塞怎么办速效办法 孕妇流黄鼻涕怎么办 孕妇有脓鼻涕怎么办 孕妇感冒黄鼻涕怎么办 单侧鼻子流黄水 怎么办 怀孕感冒流鼻涕鼻塞怎么办 小孩鼻炎黄鼻涕怎么办 孕妇流浓鼻涕怎么办 孕妇感冒鼻塞流鼻涕怎么办