node.js事件events详解

nodejs基于事件驱动

事件发布,事件订阅

  • 大多数 Node.js 核心 API 构建于惯用的异步事件驱动架构,其中某些类型的对象(又称触发器,Emitter)会触发命名事件来调用函数(又称监听器,Listener)。同步函数,异步触发,通过回调函数处理异步。

例如,net.Server 会在每次有新连接时触发事件,fs.ReadStream 会在文件被打开时触发事件,stream会在数据可读时触发事件。

所有能触发事件的对象都是 EventEmitter 类的实例。 这些对象有一个 eventEmitter.on() 函数,用于将一个或多个函数绑定到命名事件上。 事件的命名通常是驼峰式的字符串。

当 EventEmitter 对象触发一个事件时,所有绑定在该事件上的函数都会被同步地调用。

  • 这是node.js文档的一个例子:
    我的代码下载index.js文件里,通过node index执行
const EventEmitter = require('events');

class MyEmitter extends EventEmitter {}

const myEmitter = new MyEmitter();
myEmitter.on('event', () => {
  console.log('触发事件');
});
myEmitter.emit('event'); //这个就会触发myEmitter.on里的回调函数
  • 实际上这个例子不太好,我再处理一下:

放在以前,我们处理"异步"都是这样做的(需要的话会传一些参数啥的):

let fn = () => {
  console.log("数据结果的处理");
  
}
setTimeout(() => {
    console.log("得到数据");
    fn()
}, 2000);

在纯的js里,虽然可以达到预期目标,但是层层嵌套的函数实在是太让人头疼,为了减少这种麻烦,node.js的events的事件监听就能很好的解决。

  • EventEmitter,发布-订阅模式:

const EventEmitter = require("events").EventEmitter
const myEmitter = new EventEmitter

let fn = () => {
    console.log("数据结果的处理");

}
//为了好看设置的定时*********
setTimeout(() => {
    console.log("1s");
}, 1000);
setTimeout(() => {
    console.log("1.5s");
}, 1500);
setTimeout(() => {
    console.log("2s");
}, 2000);
//************************
setTimeout(() => {
    console.log("2.5s 得到数据");
    myEmitter.emit("dataMake")
}, 2500);
myEmitter.on("dataMake",fn)

在这里插入图片描述

这样,异步触发回调函数将会变得更加好归类、结构化。

用于对象

  • 让对象继承事件的EventEmitter的原型:
const EventEmitter = require("events").EventEmitter

function FnObj(params) {
    this.name = params;
}

FnObj.prototype.__proto__=EventEmitter.prototype

const obj = new FnObj("Lucy");
obj.on("ly",function() {
    console.log(this.name);
    //注意箭头函数没有this哦
})
setTimeout(() => {
    obj.emit("ly")
}, 2000);

在这里插入图片描述

这就是node.js的事件,发布-订阅模式

events使用进阶

异步执行的处理函数

虽然对函数的处理变得更加结构有序,但是异步执行总会发生一些数据竞争的情况,谁也不知道什么时候数据能被真正的准备好供异步函数使用。

或许有人会说我的处理函数在数据得到返回的函数里显示调用呢?然后现实开发中并不一定完全按照你的想法来执行,很多网站的数据并不能仅通过一个表单获取,也不一定保证两个数据的先后结果,在两个数据集合得到时间不一致或不能保证一致的情况下,异步执行就是个问题。

所以,处理好函数执行顺序则是至关重要的:

那么在此之前,你需要了解一下node.js的事件循环,我准备了相当详细的介绍:https://blog.csdn.net/qq_32842925/article/details/83446329

  • 第一步,搞清楚到底传了什么参数进去

传参数和this到你的监听器:

eventEmitter.emit() 方法可以传任意数量的参数到监听器函数。 当监听器函数被调用时,this 关键词会被指向监听器所绑定的 EventEmitter 实例

不要用箭头函数
不要用箭头函数
不要用箭头函数
重要的事情说三遍,箭头函数没有this,它根本指不到你的实例

const EventEmitter = require("events").EventEmitter

const myEmitter = new EventEmitter();
myEmitter.on('event', function (a, b) {
    console.log(a, b);
    console.log(this);
    console.log(this === myEmitter);
});
myEmitter.emit('event', '参数a', '参数b');

在这里插入图片描述

贴心的我又简单优化了node文档的打印结果,更加易读

但是如果有些人就是倔强,我就要写箭头函数,我牛逼:

const EventEmitter = require("events").EventEmitter

const myEmitter = new EventEmitter();
myEmitter.on('event',  (a, b)=> {
    console.log(a, b);
    console.log(this);
    console.log(this === myEmitter);
});
myEmitter.emit('event', '参数a', '参数b');

对比上一次的结果:
在这里插入图片描述

const EventEmitter = require("events").EventEmitter

const myEmitter = new EventEmitter();
myEmitter.on('event',  (a, b,othis)=> {
    console.log(a, b);
    console.log(othis);
    console.log(othis === myEmitter);
});
myEmitter.emit('event', '参数a', '参数b', myEmitter);

非要用箭头函数,但是这样不很麻烦吗?
在这里插入图片描述

同步还是异步,无所谓

  • 加上参数传递,一切都不是障碍了:
const EventEmitter = require("events").EventEmitter

const myEmitter = new EventEmitter();
let data = {}

myEmitter.on('event', function (data) {
    setImmediate(() => {
    	//假如这里有个数据结构化、图形化的处理函数
        console.log('异步进行结果处理');
        console.log(data.name, data.age, data.height,data.sex);
    });
});
myEmitter.emit('event', data); //虽然我立马就准备触发数据处理函数

process.nextTick(() => {
    console.log("同步的代码去获取到了数据")
    data.name = "小芳";
    data.age = "18"
} )
process.nextTick(() => {
    console.log("同步的代码去获取到了数据")
    data.height = "1米8"
})
Promise.resolve("女").then(res => {
    console.log("异步的then函数也去拿了一下数据")
    data.sex = res
})

即便我先于获取数据的函数调用了数据处理的函数,但是结合同步异步函数,数据处理得到了最后的保障:

在这里插入图片描述

也有人可能会问,你这数据暴露在全局这样做有意义吗?

回答:这里是为了调用方便,你完全可以将数据请求封装起来,让他们在局部执行,并将事件触发也放在局部作用域里,参数传递只是事件发布函数的基本功能,合理使用效果更佳!

监听事件的行为

  • 监听事件的创建:
const EventEmitter = require("events").EventEmitter
const myEmitter = new EventEmitter();

myEmitter.on('newListener',function() {
    console.log('有事件创建出来');
})
myEmitter.on('event', function () {
    console.log('事件处理函数调用');
});
// myEmitter.emit('event')
//打印:有事件创建出来

如果我重新开启事件触发,那么结果是这样
在这里插入图片描述

  • 事件解绑:
const EventEmitter = require("events").EventEmitter
const myEmitter = new EventEmitter();

function fn() {
    console.log('事件处理函数调用');
}
myEmitter.on('newListener',function() {
    console.log('有事件创建出来');
})
myEmitter.on('event', fn);
myEmitter.emit('event')
myEmitter.off('event', fn);
myEmitter.emit('event')
myEmitter.emit('event')
myEmitter.emit('event')

在这里插入图片描述

事件解绑是将回调函数与监听的事件分离,如果想上面一样直接把回调函数写在事件绑定函数里是无法解绑,下面是错误的示范
在这里插入图片描述

在这里插入图片描述

  • 监听事件的解绑:
const EventEmitter = require("events").EventEmitter
const myEmitter = new EventEmitter();

function fn() {
    console.log('事件处理函数调用');
}
myEmitter.on('newListener',function() {
    console.log('有事件创建出来');
})
myEmitter.on('removeListener', function () {
    console.log('有事件被解绑');
})
myEmitter.on('event', fn);
myEmitter.emit('event')
myEmitter.off('event', fn);
myEmitter.emit('event')
myEmitter.emit('event')
myEmitter.emit('event')

在这里插入图片描述

  • 或者只让事件执行一次:
const EventEmitter = require("events").EventEmitter
const myEmitter = new EventEmitter();

function fn() {
    console.log('事件处理函数调用');
}
myEmitter.on('newListener',function() {
    console.log('有事件被创建1');
})
myEmitter.on('newListener',function() {
    console.log('有事件被创建2');
})
myEmitter.on('removeListener', function () {
    console.log('有事件被解绑');
})
myEmitter.once('event', fn);
console.log('1111111111111111111111');
myEmitter.emit('event')
console.log('2222222222222222222222');
myEmitter.emit('event')
myEmitter.emit('event')
myEmitter.emit('event')

在这里插入图片描述

两个事件监听器弄清楚流程:
事件新增监听器1被创建,但是当时没有监听事件创建的监听器;
事件新增监听器2被创建,触发了事件创建监听器1,打印:事件被创建1;
事件移除监听器被创建,触发事件创建监听器1,2,打印:事件被创建1,事件被创建2;
事件’event’创建一次性事件,触发事件创建监听器1,2,打印:事件被创建1,事件被创建2;
log打印:11111111111111111;
emit触发事件event,触发解绑事件监听器,处理事件队列里的event事件,
打印:有事件被解绑,事件处理函数被调用;
log打印:22222222222222222;
由于事件event被解绑,无法被调用,后面没有打印结果。。。

这里需要注意的就是,nodejs里的事件队列的事件是在监听创建触发后再执行事件的触发

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

奔跑的飞牛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值