nodejs之EventEmitter实现
Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。
Node.js 里面的许多对象都会分发事件:一个 net.Server 对象会在每次有新连接时触发一个事件, 一个 fs.readStream 对象会在文件被打开的时候触发一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。
以下简单实现:
- function EventEmitter() {
- this.events = {}
- this.counts = 0
- this.maxNum = 10
- // 内置事件:newListener -> 事件在添加新监听器时被触发。removeListener -> 事件在接除监听器时被触发。
- this.innerEvent = {
- NEWLISTENER: 'newListener',
- REMOVELISTENER: 'removeListener'
- }
- }
- // 为指定事件添加一个监听器到监听器数组的尾部。
- function addListener(eventName, callback) {
- if (typeof callback !== 'function') return
- if (!this.events[eventName]) {
- if (!this._isInnerEvent(eventName)) {
- this.events[eventName] = [{ type: 'on', callback }]
- this.counts++
- if (this.counts > this.maxNum) {
- console.warn(`目前监听器数量:${this.counts}个,监听器已经超过${this.maxNum}个`)
- }
- if (this.events[this.innerEvent.NEWLISTENER]) {
- this.emit(this.innerEvent.NEWLISTENER, eventName)
- }
- } else {
- this.events[eventName] = { type: 'on', callback }
- }
- } else {
- this.events[eventName].push({ type: 'on', callback })
- }
- }
- EventEmitter.prototype = {
- _toString: function (obj) {
- return Object.prototype.toString.call(obj).slice(8, -1)
- },
- _isInnerEvent: function (eventName) {
- return !!this.innerEvent[eventName.toUpperCase()]
- },
- addListener: addListener,
- on: addListener,
- // 按监听器的顺序执行执行每个监听器,如果事件有注册监听返回 true,否则返回 false。
- emit: function () {
- let arg = Array.prototype.slice.call(arguments)
- let eventName = arg[0]
- let params = arg.slice(1)
- if (!this._isInnerEvent(eventName)) {
- if (this._toString(this.events[eventName]) === 'Array' && this.events[eventName].length) {
- this.events[eventName].forEach(event => {
- let { type, callback } = event
- callback.apply(null, params)
- if (type === 'once') {
- this.events[evtName].splice(index, 1)
- }
- })
- return true
- }
- return false
- } else {
- this.events[eventName].callback.apply(null, params)
- if (this.events[eventName].type === 'once') {
- delete this.events[eventName]
- }
- }
- },
- // 为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。
- once: function (eventName, callback) {
- if (typeof callback !== 'function') return
- if (!this.events[eventName]) {
- if (!this._isInnerEvent(eventName)) {
- this.events[eventName] = [{ type: 'once', callback }]
- this.counts++
- if (this.counts > this.maxNum) {
- console.warn(`目前监听器数量:${this.counts}个,监听器已经超过${this.maxNum}个`)
- }
- if (this.events[this.innerEvent.NEWLISTENER]) {
- this.emit(this.innerEvent.NEWLISTENER, eventName)
- }
- } else {
- this.events[eventName] = { type: 'once', callback }
- }
- } else {
- this.events[eventName].push({ type: 'once', callback })
- }
- },
- // 移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。
- removeListener: function (eventName, callback) {
- if (this._toString(this.events[eventName]) === 'Array') {
- let _events = this.events[eventName].concat()
- for (let i = 0; i < _events.length; i++) {
- if (_events[i].callback === callback) {
- this.events[eventName].splice(i, 1)
- return
- }
- }
- }
- },
- // 移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。
- removeAllListeners: function (eventName) {
- if (eventName) {
- if (this.events[eventName]) {
- delete this.events[eventName]
- if (!this._isInnerEvent(eventName)) {
- this.counts--
- if (this.events[this.innerEvent.REMOVELISTENER]) {
- this.emit(this.innerEvent.REMOVELISTENER, eventName)
- }
- }
- }
- } else {
- this.events = {}
- this.counts = 0
- }
- },
- // 默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于提高监听器的默认限制的数量。
- setMaxListeners: function (num) {
- this.maxNum = num
- },
- // 返回指定事件的监听器数组。
- listeners: function (eventName) {
- if (this._toString(this.events[eventName]) === 'Array') {
- let _events = this.events[eventName].concat()
- let newArray = []
- _events.forEach(item => {
- newArray.push(item.callback)
- })
- return newArray
- }
- },
- // 返回指定事件的监听器数量
- listenerCount: function (eventName) {
- if (this._toString(this.events[eventName]) === 'Array') {
- return this.events[eventName].length
- }
- }
- }
- var eventEmit = new EventEmitter()
- eventEmit.on('newListener', function newListener(eventName) {
- console.log('>>>>newListener ---', eventName)
- })
- eventEmit.on('removeListener', function removeListener(eventName) {
- console.log('>>>>removeListener ---', eventName)
- })
- console.log(eventEmit)
- function event1() {
- console.log('event1')
- }
- eventEmit.on('event', event1)
- eventEmit.on('event1', event1)
- eventEmit.on('event', function event2() {
- console.log('event2')
- })
- eventEmit.emit('event')
- eventEmit.emit('event1')
nodejs之EventEmitter实现的更多相关文章
- nodejs基础 -- EventEmitter
var events = require('events'); nodejs所有的异步I/O操作在完成时都会发送一个事件到事件队列 nodejs里面的许多对象都会分发事件,如: 一个net.Serve ...
- nodejs中EventEmitter
在模块events中,定义了一个EventEmitter类,可以使用var EventEmitter = require('events');访问它.基本上所有发送事件的对象都是继承自EventEmi ...
- nodejs 事件EventEmitter
index.js: // 引入 events 模块 var events = require('events'); //处理函数要写在调用前 var eventHandler = function() ...
- 深入浅出NodeJS——数据通信,NET模块运行机制
互联网的运作,最根本的驱动就是信息的交互,NodeJS 在数据交互这一块做的很带感,异步编程让人很惬意,关于 NodeJS 的数据通信,最基础的两个模块是 NET 和 HTTP,前者是基于 TCP 的 ...
- 深入理解nodejs的异步IO与事件模块机制
node为什么要使用异步I/O 异步I/O的技术方案:轮询技术 node的异步I/O nodejs事件环 一.node为什么要使用异步I/O 异步最先诞生于操作系统的底层,在底层系统中,异步通过信号量 ...
- backbone event 事件订阅 和发布 源码小读
nodejs有eventEmitter 类,想到backbone 有个event模块 可以对对象做事件绑定和触发,是backbone的核心模块. backbone event模块 on 添加自定义事 ...
- webpack-插件机制杂记
系列文章 Webpack系列-第一篇基础杂记 webpack系列-插件机制杂记 前言 webpack本身并不难,他所完成的各种复杂炫酷的功能都依赖于他的插件机制.或许我们在日常的开发需求中并不需要自己 ...
- [转] webpack之plugin内部运行机制
简介 webpack作为当前最为流行的模块打包工具,几乎所有的主流前端开发框架(React.Vue等)都会将其作为默认的模块加载和打包工具.通过简单的配置项,使用各种相关的loader和plugin, ...
- .12-浅析webpack源码之NodeWatchFileSystem模块总览
剩下一个watch模块,这个模块比较深,先大概过一下整体涉及内容再分部讲解. 流程图如下: NodeWatchFileSystem const Watchpack = require("wa ...
随机推荐
- Linux切换超级管理员root用户
Ubuntu用$标志表示你现在处于普通用户,#表示超级用户. 普通用户会有限制,想从普通变成超级用户,可以输入 su 或 su - 命令,要求你输入密码, 你如记得密码就可以直接输入,再Enter即可 ...
- 关于Java中length、length()、size()的区别
length——数组的属性: length()——String的方法: size()——集合/映射的方法:(List.Set.Map) 转载自https://blog.csdn.net/qq_3323 ...
- 四分位数与pandas中的quantile函数
四分位数与pandas中的quantile函数 1.分位数概念 统计学上的有分位数这个概念,一般用p来表示.原则上p是可以取0到1之间的任意值的.但是有一个四分位数是p分位数中较为有名的. 所谓四分位 ...
- mysql经典面试必须知道的
http://www.cnblogs.com/wangshouchang/p/6930443.html 在华三的时候就问道了数据集的事务的四种特性,事务的隔离级别,事务的存储过程等
- Jmeter系列(32)- 详解 CSV 数据文件设置
如果你想从头学习Jmeter,可以看看这个系列的文章哦 https://www.cnblogs.com/poloyy/category/1746599.html 了解一哈什么是 CSV 文件 为了实现 ...
- Spring Bean各阶段生命周期的介绍
一.xml方式配置bean 二.Aware接口 2.1 BeanNameAware 2.2 BeanFactoryAware 2.3 ApplicationContextAware 2.4 Aware ...
- 图解resilience4j容错机制
Resilience4j是一个轻量级.易于使用的容错库,其灵感来自Netflix Hystrix,但专为Java 8和函数式编程设计.轻量级,因为库只使用Vavr,它没有任何其他外部库依赖项.相比之下 ...
- JavaScript基础对象创建模式之对象的常量(028)
虽然许多编程语言提供了const关键字来支持常量的声明,但JavaScript里没有表示常量的语义.我们可以用全大写的方式来声明变量,表明它实际上是个常量: Math.PI; // 3.1415926 ...
- Yarn的安装和全局配置(源/缓存位置/全局安装位置)
本文安装环境: Win10 64位 前置条件: 已安装好Node环境(参考Node安装与环境配置) 下载和安装 Yarn安装包下载地址 全局配置 控制台输入命令, 正常显示版本表示安装成功 $ yar ...
- SpringBoot2.x入门教程:理解配置文件
前提 这篇文章是<SpringBoot2.x入门>专辑的第4篇文章,使用的SpringBoot版本为2.3.1.RELEASE,JDK版本为1.8. 主要介绍SpringBoot配置文件一 ...