前言

本文会介绍 events 模块的主要作用和使用方式,以及自己实现一个简单的发布订阅模式,帮助加深理解。

events 模块

文档地址,点击访问。

Node.js 是基于事件驱动实现的异步操作。 事件驱动依赖就是的 events 模块。

events 模块导出一个 EventEmitter 类,它是发布订阅模式的一种实现。

无论你是了解发布订阅模式,只要你有过前端开发的经验,那么对于 DOM事件监听,事件解绑等操作一定不陌生。其实这就是一种发布订阅。

平时开发中我们很少会直接使用这个模块。这是因为 Node.js 的很多内置模块,比如 fs , http 等模块,都是继承自 EventEmitter 类而实现的。

也就是说,我们平时经常用到的模块,就已经具备了发布订阅(事件监听、事件触发)的能力。后面会介绍到 Stream 和 http 模块的使用,到时候就会发现,到处都有发布订阅的影子。

EventEmitter 类

events 模块对外暴露 EventEmitter 类。

打印一下它的原型对象:

const EventEmitter = require('events')console.log(EventEmitter.prototype)

有很多方法,光看名字我们就能知道每个方法的作用是什么:

  • on:订阅事件
  • emit:发布事件
  • once:只订阅一次,也就是只能触发一次
  • removeListener:移除订阅者
  • removerAllListeners:移除所有的订阅者
  • off:removeLister 方法的别名

使用

EventEmitter 类的使用方式有两种。

首先它是一个构造函数(源码中以构造函数实现),所以可以通过实例化的方法来使用。

其次,node 的很多模块都是继承自它,所以子类除了自身的能力之外,也具备了发布订阅的能力。

这里演示下第二种。

const EventEmitter = require('events')class Reader extends EventEmitter {// ...
}const reader = new Reader()const buy = bookName => {console.log('买新书:', bookName)
}// 订阅小册上新的事件;当有上新后,就买买买
reader.on('new', buy)// 小册上新了
reader.emit('new', '《TypeScript 全面进阶指南》') // 买新书: 《TypeScript 全面进阶指南》
// 小册又上新了
reader.emit('new', '《NestJS 项目实战》') // 买新书: 《《NestJS 项目实战》》// 最近不打算买书了: 取消订阅
reader.off('new', buy)// 小册上新了。但是取消了订阅,所以消息不会发给你了
reader.emit('new', 'React 上天入地》') // 没有效果

实现发布订阅

实现发布订阅,也是常考的一道面试题。

实现起来也并不复杂,结合注释很方便理解。

// 定义一个构造函数
function EventEmitter() {//  存储订阅者,也就是事件处理函数的容器。// 数据结构为:{ event_type1: [handler1, ...], ... }this._events = Object.create(null) //
}// 订阅事件的方法
EventEmitter.prototype.on = function on(type, handler) {// 首先判断要注册的事件对应的容器是否存在,若存在则将新的 handler 存入,否则先创建一个对应的容器let events = this._events[type]if (!events) {events = this._events[type] = []}events.push(handler)
}// 只订阅一次事件的方法
// 思路:利用切片编程的思想,给原始 handler 包装一层。
// 正常的执行逻辑:handler 执行
// 切片编程的逻辑:handler 执行,执行完再调用一次 off 。相当于扩展了 handler 方法
EventEmitter.prototype.once = function on(type, handler) {const fn = (...args) => {handler(...args)this.off(type, fn)}this.on(type, fn)
}// 发布事件的方法
EventEmitter.prototype.emit = function emit(type, ...args) {// 从事件容器中取出对应的 handler 依次去执行// 若发布的事件不存在,则不进行处理let events = this._events[type]// events可能存在空数组的情况,需要处理if (!events || events.length === 0) {return}events.forEach(handler => handler(...args))
}// 移除事件监听
EventEmitter.prototype.off = function off(type, handler) {let events = this._events[type]if (!events || events.length === 0) {return}// 过滤掉容器中需要解除监听的方法this._events[type] = events.filter(item => handler !== item)
}// 测试
const emitter = new EventEmitter()const play = (...args) => {console.log('play', ...args)
}const sing = (...args) => {console.log('sing', ...args)
}emitter.on('play', play)
emitter.on('sing', sing)emitter.emit('sing', 'rap')  // sing rap
emitter.emit('play', '篮球')  // play 篮球emitter.off('play', play)
emitter.emit('play')const dance = () => {console.log('dance')
}emitter.once('dance', dance)emitter.emit('dance') // dance
emitter.emit('dance') // 已移除订阅,不执行

延申阅读:eventbus 和 mitt

有一道经典的面试题:Vue 组件通信的方式有哪些?

除了常见的通过属性,自定义事件,借助状态库,在 Vue2 中还有一种方式,叫作 eventBus 事件总线。

EventBus 本质上就是一个发布订阅模式。但是在 Vue3 中被移除了,官方推荐使用一个第三方库 mitt 来做发布订阅。

Mitt是一个十分小巧的发布订阅库,大约只有200字节左右。而且用法上也没有什么太大的差异,稍微看一下文档就会用了。

import mitt from 'mitt'const emitter = mitt()// 订阅事件
emitter.on('foo', e => console.log('foo', e) )// 订阅所有的事件
emitter.on('*', (type, e) => console.log(type, e) )// 触发事件
emitter.emit('foo', { a: 'b' })// 清除所有的订阅者
emitter.all.clear()// 使用事件处理函数的引用,方便移除监听
function onFoo() {}
emitter.on('foo', onFoo)   // listen
emitter.off('foo', onFoo)  // unlisten

总结

本文介绍了 Node.js 中 events 模块的使用,它主要导出一个 EventEmitter 类来做发布订阅。Node.js 的很多核心模块都继承自 EventEmitter 类。

它是一个底层的模块,但通常很少直接使用它。等后面讲到 Stream,再后面讲到 http 模块的时候,就能体会到它的存在了。

额外提一嘴,手写实现,并不是要重复造轮子,主要是为了加深对原生方法的理解。

当然,如果有一个非常巧妙的设计和实现,那早晚也会大火的。

Node.js 入门:events 模块和发布订阅模式相关推荐

  1. RabbitMQ入门学习系列(四) 发布订阅模式

    什么时发布订阅模式 把消息发送给多个订阅者.也就是有多个消费端都完整的接收生产者的消息 换句话说 把消息广播给多个消费者 消息模型的核心 RabbitMQ不发送消息给队列,生产者也不知道消息发送到队列 ...

  2. js设计模式之观察者模式和发布/订阅模式

    观察者模式 The Observer is a design pattern where an object (known as a subject) maintains a list of obje ...

  3. 设计模式之发布订阅模式

    发布--订阅模式简介 发布--订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,多个观察者对象都依赖于一个目标对象,当目标对象的状态发生变化时,所有依赖于这个对象的观察者对象都会收到通知. ...

  4. Node.js入门教程二之模块的使用

    模块化使用的定义 什么是模块化 把代码按照逻辑和功能封装成为各种不同的模块就是代码的模块化. 模块化使用的优点 通过把代码的各自封装,相互独立,降低代码的耦合性的同时,可以自行决定引入执行那些外部模块 ...

  5. node.js入门 - 9.api:http

    node一个重要任务是用来创建web服务,接下来我们就学习与此相关的一个重要的api -- http.我们使用http.createServer()创建一个http服务的实例,用来处理来自客户的请求. ...

  6. Node.js 入门到干活,10 个优质项目就够了!

    Node.js 在很多大公司都有不错的实践,比如:淘宝.天猫 Web 版,很多页面都是在 Node 服务器上渲染的.还有各种脚手架.前端打包发布工具.构建生态的小工具,也基本都是 Node.js 编写 ...

  7. Node.js 入门知识点总结

    Noed.js 快速入门 前言 node.js 入门 环境配置 hello node.js node.js 获取参数 node.js 模块系统 node.js 路由 node.js 文件操作 使用 n ...

  8. html js不触发_「万字整理 」这里有一份Node.js入门指南和实践,请注意查收 ??

    前言 什么是 Node.js 呢 ? JS 是脚本语言,脚本语言都需要一个解析器才能运行.对于写在 HTML 页面里的 JS,浏览器充当了解析器的角色.而对于需要独立运行的 JS,NodeJS 就是一 ...

  9. node.js中模块_在Node.js中需要模块:您需要知道的一切

    node.js中模块 by Samer Buna 通过Samer Buna 在Node.js中需要模块:您需要知道的一切 (Requiring modules in Node.js: Everythi ...

  10. node.js入门 - 2.创建一个简单聊天室

    这篇文章将通过开发一个简单聊天室的方式,介绍node.js的net模块. 一.第一版,只向客户端发送信息   我们先实现一个简单的版本,代码如下: var net=require('net'); va ...

最新文章

  1. SpringBoot如何自动生成实体类和Dao层以及映射文件(mybatis generator 自动生成代码)
  2. 关于TextBox控件字体颜色绑定
  3. Linux下安装Jboss
  4. ITK:创建矢量图像
  5. SAP CRM产品主数据里的七种ID
  6. 分布式文件系统(HDFS)与 linux系统文件系统 对比
  7. JavaScript中带有示例的Math.PI属性
  8. [css] 什么是zoom?它有什么作用?
  9. idea报错:Error: java: 错误: 不支持发行版本 5
  10. 【码云周刊第 61 期】四款开源项目教你玩转微信游戏
  11. js获取数组最大值的索引_数组中最大值的返回索引
  12. python requests请求终止_Requests 如何中断请求?
  13. AE倒影插件:vc reflect插件汉化版(支持ae2020)
  14. NXP MC9S12中断寄存器配置说明
  15. 【源码开放】Hexo+Github+Coding 博客butterfly 和 matery 主题 搭建完全教程【整理】
  16. 3533: 黑白图像
  17. neatdm路径_网易有爱插件设置教程-网易有爱插件游戏路径如何设置
  18. hypot函数、fmod函数
  19. python英文发音-用Python写一个背英文单词程序
  20. 借助阿里云轻松部署企业网盘

热门文章

  1. 如何进入训练模式_为何孩子学习痛苦又低效?巧用这2个训练方法,孩子学习更高效...
  2. 全民一起VBA实战篇 专题4 第七回 递归不怕检索难,万缕千层只等闲
  3. GIS入门进阶007
  4. FPGA入门(7):IP核调用
  5. SqlServer查询哪些表里有数据
  6. Linux入门教程:P11->文件查找类
  7. golang api项目_如何创建Golang REST API:项目布局配置[第2部分]
  8. Feign接口封装成独立的模块对外提供能力
  9. NtCreatFile函数的参数传递分析 - zl21 - 程序员网志 - Powered By PHPWind.Net
  10. 基于直方图的可逆信息脆弱水印算法--matlab实现