浅谈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 通过json自动生成Dom的代码
Apr 01 Javascript
Chosen 基于jquery的选择框插件使用方法
May 30 Javascript
js加入收藏以及使用Jquery更改透明度
Jan 26 Javascript
用jquery实现动画跳到顶部和底部(这个比较简单)
Sep 01 Javascript
javascript实现拖放效果
Dec 16 Javascript
js格式化输入框内金额、银行卡号
Feb 01 Javascript
jQuery实现字符串全部替换的方法
Dec 12 Javascript
js 判断数据类型的几种方法
Jan 13 Javascript
Angular2安装angular-cli
May 21 Javascript
Bootstrap Paginator+PageHelper实现分页效果
Dec 29 Javascript
js最实用string(字符串)类型的使用及截取与拼接详解
Apr 26 Javascript
JS localStorage存储对象,sessionStorage存储数组对象操作示例
Feb 15 Javascript
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
星际中一些鲜为人知的详细资料
2020/03/04 星际争霸
PHP 单引号与双引号的区别
2009/11/24 PHP
老生常谈PHP面向对象之标识映射
2017/06/21 PHP
thinkPHP框架实现的无限回复评论功能示例
2018/06/09 PHP
Yii框架布局文件的动态切换操作示例
2019/11/11 PHP
javascript 字符串连接的性能问题(多浏览器)
2008/11/18 Javascript
Javascript学习笔记2 函数
2010/01/11 Javascript
JS限制文本框只能输入数字和字母方法
2015/02/28 Javascript
javascript通过元素id和name直接取得元素的方法
2015/04/28 Javascript
浅谈jQuery中Ajax事件beforesend及各参数含义
2016/12/03 Javascript
简单实现js进度条加载效果
2020/03/25 Javascript
详解基于Angular4+ server render(服务端渲染)开发教程
2017/08/28 Javascript
详解mpvue开发小程序小总结
2018/07/25 Javascript
微信小程序整合使用富文本编辑器的方法详解
2019/04/25 Javascript
JavaScript实现点击图片换背景
2020/11/20 Javascript
Tornado协程在python2.7如何返回值(实现方法)
2017/06/22 Python
PyQt5响应回车事件的方法
2019/06/25 Python
python利用7z批量解压rar的实现
2019/08/07 Python
Python字符串的修改方法实例
2019/12/19 Python
python 日志模块logging的使用场景及示例
2021/01/04 Python
css3动画效果抖动解决方法
2018/09/03 HTML / CSS
意大利高端时尚买手店:Stefania Mode
2018/03/01 全球购物
Mountain Warehouse波兰官方网站:英国户外品牌
2019/08/29 全球购物
什么是规则表达式
2012/05/03 面试题
历史系毕业生自荐信
2013/10/28 职场文书
如何写你的创业计划书
2014/01/07 职场文书
消防器材管理制度
2014/01/28 职场文书
董事长秘书职责
2014/01/31 职场文书
实验教师岗位职责
2014/02/13 职场文书
企业宣传工作方案
2014/06/02 职场文书
2014社会治安综合治理工作总结
2014/12/04 职场文书
优秀少先队辅导员事迹材料
2014/12/24 职场文书
个人思想政治总结
2015/03/05 职场文书
幼儿园托班开学寄语(2015秋季)
2015/05/27 职场文书
Nginx+Tomcat负载均衡集群的实现示例
2021/10/24 Servers
全网非常详细的pytest配置文件
2022/07/15 Python