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 ...
随机推荐
- 为什么Web开发人员在2020年不用最新的CSS功能
转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 原文出处:https://dzone.com/articles/why-masses-are-not-usi ...
- 【服务器】VMware Workstation Pro虚拟机搭建本地服务器CentOs7和宝塔面板(保姆式教程)
内容繁多,请耐心跟着流程走,在过程中遇到问题请在下面留言. 前言 这几天一直在复习thinkphp5.1,学习环境是phpStudy8.1,但是遇到了文件有缓存的问题(thinkphp5.1.39,修 ...
- SSM-框架搭建-tank后台学习系统
一.前言 最近收到很多网友给我私信,学习软件开发有点吃力,不知道从何处开始学习,会点基础但是做不出来什么项目, 都想放弃了.我就回复道:当下互联网飞速发展,软件开发行业非常吃香而且前景相当不错.希望能 ...
- Perl入门(四)Perl的正则表达式
正则表达式是Perl语言的特色,基本的语法不是很难,但是编写一个符合需求.高效的正则表达式,还是有一些挑战的. Perl的三种匹配模式 1.查找 语法:m/正则表达式内容/; 作用:查找匹配内容中是否 ...
- JSP新闻显示
MYSQL数据库创建新闻表,用户登陆时使用SERVLET获取用户名,效验通过后直接跳转新闻列表页面,JSP使用EL显示新闻列表 1.首先创建数据库及用户.新闻表 CREATE DATABASE /*! ...
- Java笔试面试总结—try、catch、finally语句中有return 的各类情况
前言 之前在刷笔试题和面试的时候经常会遇到或者被问到 try-catch-finally 语法块的执行顺序等问题,今天就抽空整理了一下这个知识点,然后记录下来. 正文 本篇文章主要是通过举例的方式来阐 ...
- 入门大数据---HBase Shell命令操作
学习方法 可以参考官方文档的简单示例来 点击查看 可以直接在控制台使用help命令查看 例如直接使用help命令: 从上图可以看到,表结构的操作,表数据的操作都展示了.接下来我们可以针对具体的命令使用 ...
- C++的新手入门答疑
基本部分: .ctrl+f5 调试不运行,会出现press anykey to continue f5 调试 .c++变c,修改Stdafx.h,将#include<stdio.h>替换为 ...
- jQuery控制倒计时
1.1 秒杀的倒计时 做秒杀网页总免不了倒计时,但没有很好的服务器,啥资源都没有,只能将部分任务交给浏览器去处理,比如秒杀首页的倒计时,因为真正秒杀是在具体页面,首页只是展示而已,所以误差一点是允许的 ...
- .NET 开源工作流: Slickflow流程引擎高级开发(七)--消息队列(RabbitMQ)的集成使用
前言:工作流流程过程中,除了正常的人工审批类型的节点外,事件类型的节点处理也尤为重要.比如比较常见的事件类型的节点有:Timer/Message/Signal等.本文重点阐述消息类型的节点处理,以及实 ...