浅谈node的事件机制


Posted in Javascript onOctober 09, 2017

Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.

在nodejs的官方文档中,明确写出了node的一个特性是event-driven(事件驱动),可见其非常重要。查看源码,我们可知其事件机制为用js写的EventEmitter类,写得非常优雅且应用了发布/订阅模式。

通过实现一个简易的、具有发布/订阅模式的事件机制,以此来理清EventEmitter类的实现思路

Publish/Subscribe(发布/订阅模式)

类比

要讲的是一个模式,模式这个词,听起来就很抽象。我们先举个栗子。假设有一家报纸机构,提供晨报、午报、晚报。如果你想要看某种报纸,你需要向报纸机构订阅,等对应的报纸发布出来后,报纸机构就会通知你来拿报纸。

在这个过程中,报纸机构实现了两个功能,一是接受客户的订阅;二是发布不同类型的报纸。发布报纸的时候,订阅该类型报纸的客户就能接收到通知。

这个报纸机构也就是我们要实现的事件机制。

浅谈node的事件机制

目的

从上面的例子可以看出:1.发布报纸;2.将报纸给到客户;这个连续的过程由于报纸机构的存在,变成了可以先订阅,再发布,等到发布就自动送到客户手中,实现了动作时间上的分离。这也是发布/订阅系统的优势。

实现思路

我们有3种报纸,对应3个事件,每个事件发生时要通知客户。对应的数据格式可以如下:

var Event = {
 morning: event1,
 noon: event2,
 night: event3
}

由于每种报纸都可能有不止一个人订阅,那么格式可优化成这样:

var Event = {
 morning: [e11, e12,...],
 noon: [e21, e22],
 night: event3
}

当用户订阅的时候,我们就将其事件添加对应的数组中;当事件发布的时候,就执行相应事件。说白了就是先存储后使用。

具体代码如下:

1.on表示订阅,将事件添加到对应数组中
2.emit表示发布,将对应数组中的数据取出来执行
3.off表示删除无用的事件

var Event = {
 on: function(key, listener) {
  if (!this.__events) {
   this.__events = {}
  }
  if (!this.__events[key]) {
   this.__events[key] = [];
  } 
  if (_indexOf(this.__events[key], listener) == -1 && typeof listener === 'function') {
   this.__events[key].push(listener)
  }
 },
 emit: function(key) {
  if (!this.__events || !this.__events[key]) return 
  //取得每次订阅不同的参数
  var arg = Array.prototype.slice.call(arguments, 1) || [];

  var listeners = this.__events[key];
  var len = listeners.length;

  for (var i=0; i<len; i++) {
   listeners[i].apply(this, arg)
  }
  return this

 },
 off: function(key, listener) {
  if (!key && !listener) {
   this.__events = {}
  }
  if (key && !listener) {
   delete this.__events[key]
  }
  if (key && listener) {
   var listeners = this.__events[key];
   var index = _indexOf(listeners, listener);
   (index > -1) && listeners.splice(index, 1);
  }
  return this
 }
}

var _indexOf = function(array,key){
 if (array === null) return -1
 var i = 0, length = array.length
 for (; i < length; i++) if (array[i] === key) return i
 return -1
}
//调用
Event.on('console1', function(num) {
 console.log(num); // 1
});

Event.emit('console1', 1)

node的EventEmitter

node的EventEmitter基本逻辑和上面提供的例子基本一样,只是更加复杂些。

1.订阅事件on

function _addListener(target, type, listener, prepend) {
 var m;
 var events;
 var existing;

 if (typeof listener !== 'function')
  throw new TypeError('"listener" argument must be a function');

 events = target._events;
 ...
 if (typeof existing === 'function') {
   // Adding the second element, need to change to array.
   existing = events[type] =
    prepend ? [listener, existing] : [existing, listener];
  } else {
   // If we've already got an array, just append.
   if (prepend) {
    existing.unshift(listener);
   } else {
    existing.push(listener);
   }
  }

 return target;
}

EventEmitter.prototype.addListener = function addListener(type, listener) {
 return _addListener(this, type, listener, false);
};

EventEmitter.prototype.on = EventEmitter.prototype.addListener;

2.发布事件

EventEmitter.prototype.emit = function emit(type) {
 ...
 handler = events[type];
 switch (len) {
  // fast cases
  case 1:
   emitNone(handler, isFn, this);
   break;
  case 2:
   emitOne(handler, isFn, this, arguments[1]);
   break;
  case 3:
   emitTwo(handler, isFn, this, arguments[1], arguments[2]);
   break;
  case 4:
   emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]);
   break;
  // slower
  default:
   args = new Array(len - 1);
   for (i = 1; i < len; i++)
    args[i - 1] = arguments[i];
   emitMany(handler, isFn, this, args);
 }
}

讲到这里,相信大家已经明白EventEmitter的实现思路。

参考资料

node events.js

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript中对对层的控制
Dec 29 Javascript
json跟xml的对比分析
Jun 10 Javascript
SWFObject Flash js调用类
Jul 08 Javascript
xml转json的js代码
Aug 28 Javascript
js前台分页显示后端JAVA数据响应
Mar 18 Javascript
Bootstrap table分页问题汇总
May 30 Javascript
基于d3.js实现实时刷新的折线图
Aug 03 Javascript
Vue.js -- 过滤器使用总结
Feb 18 Javascript
bootstrap 设置checkbox部分选中效果
Apr 20 Javascript
gulp教程_从入门到项目中快速上手使用方法
Sep 14 Javascript
Vue实现兄弟组件间的联动效果
Jan 21 Javascript
vue+element UI实现树形表格
Dec 29 Vue.js
JS实现中文汉字按拼音排序的方法
Oct 09 #Javascript
ES6中的Promise代码详解
Oct 09 #Javascript
js+html5生成自动排列对话框实例
Oct 09 #Javascript
jQuery实现获取table中鼠标click点击位置行号与列号的方法
Oct 09 #jQuery
详解如何让Express支持async/await
Oct 09 #Javascript
jQuery插件artDialog.js使用与关闭方法示例
Oct 09 #jQuery
Node.JS使用Sequelize操作MySQL的示例代码
Oct 09 #Javascript
You might like
新浪微博API开发简介之用户授权(PHP基础篇)
2011/09/25 PHP
thinkphp实现面包屑导航(当前位置)例子分享
2014/05/10 PHP
Smarty模板变量与调节器实例详解
2019/07/20 PHP
推荐:极酷右键菜单
2006/11/29 Javascript
javascript简易缓动插件(源码打包)
2012/02/16 Javascript
Raphael带文本标签可拖动的图形实现代码
2013/02/20 Javascript
使用PHP+JQuery+Ajax分页的实现
2013/04/23 Javascript
node.js中的fs.readdir方法使用说明
2014/12/17 Javascript
HTML5实现留言和回复页面样式
2015/07/22 Javascript
jQuery实现网页顶部固定导航效果代码
2015/12/24 Javascript
基于javascript html5实现多文件上传
2016/03/03 Javascript
jQuery的图片轮播插件PgwSlideshow使用详解
2016/08/11 Javascript
Angular4编程之表单响应功能示例
2017/12/13 Javascript
JS运动特效之同时运动实现方法分析
2018/01/24 Javascript
Swiper 4.x 使用方法(移动端网站的内容触摸滑动)
2018/05/17 Javascript
详解vuex之store源码简单解析
2019/06/13 Javascript
vue项目打包为APP,静态资源正常显示,但API请求不到数据的操作
2020/09/12 Javascript
[06:45]DOTA2-DPC中国联赛 正赛 Magma vs LBZS 选手采访
2021/03/11 DOTA
讲解Python中的递归函数
2015/04/27 Python
python中pylint使用方法(pylint代码检查)
2018/04/06 Python
pandas数值计算与排序方法
2018/04/12 Python
python科学计算之narray对象用法
2019/11/25 Python
如何基于python操作json文件获取内容
2019/12/24 Python
python使用rsa非对称加密过程解析
2019/12/28 Python
python logging设置level失败的解决方法
2020/02/19 Python
Python之字典对象的几种创建方法
2020/09/30 Python
Python+kivy BoxLayout布局示例代码详解
2020/12/28 Python
CSS3媒体查询(Media Queries)介绍
2013/09/12 HTML / CSS
HTML5制作3D爱心动画教程 献给女友浪漫的礼物
2014/11/05 HTML / CSS
普通大学毕业生自荐信
2013/11/04 职场文书
大学生职业规划范文:象牙塔生活的四年计划
2014/01/14 职场文书
初中作文评语大全
2014/04/23 职场文书
2014年酒店前台工作总结
2014/11/14 职场文书
解决Nginx 配置 proxy_pass 后 返回404问题
2021/03/31 Servers
python爬取企查查企业信息之selenium自动模拟登录企查查
2021/04/08 Python
Win11如何查看显卡型号 Win11查看显卡型号的方法
2022/08/14 数码科技