浅谈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 相关文章推荐
jQuery autocomplate 自扩展插件、自动完成示例代码
Mar 28 Javascript
js控制鼠标事件移动及移出效果显示
Oct 19 Javascript
JavaScript与jQuery实现的闪烁输入效果
Feb 18 Javascript
jQuery移动端图片上传组件
Jun 12 Javascript
Jquery Easyui选项卡组件Tab使用详解(10)
Dec 18 Javascript
js 只比较时间大小的实例
Oct 26 Javascript
JSON基本语法及与JavaScript的异同实例分析
Jan 04 Javascript
jQuery实现小火箭返回顶部特效
Feb 03 jQuery
react国际化化插件react-i18n-auto使用详解
Mar 31 Javascript
JS数组的常用10种方法详解
May 08 Javascript
Javascript如何实现扩充基本类型
Aug 26 Javascript
JavaScript 实现轮播图特效的示例
Nov 05 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
cache_lite试用
2007/02/14 PHP
什么是MVC,好东西啊
2007/05/03 PHP
php为字符串前后添加指定数量字符的方法
2015/05/04 PHP
php自动更新版权信息显示的方法
2015/06/19 PHP
php数据序列化测试实例详解
2017/08/12 PHP
PHP设计模式(四)原型模式Prototype实例详解【创建型】
2020/05/02 PHP
一个cssQuery对象 javascript脚本实现代码
2009/07/21 Javascript
JQuery上传插件Uploadify使用详解及错误处理
2010/04/27 Javascript
关于extjs4如何获取grid修改后的数据的问题
2013/08/07 Javascript
浅析Cookie中的Path与domain
2013/12/18 Javascript
JavaScript语言对Unicode字符集的支持详解
2014/12/30 Javascript
AngularJs bootstrap搭载前台框架——js控制部分
2016/09/01 Javascript
使用jquery datatable和bootsrap创建表格实例代码
2017/03/17 Javascript
jQuery Ajax使用FormData上传文件和其他数据后端web.py获取
2017/06/11 jQuery
微信小程序图片宽100%显示并且不变形
2017/06/21 Javascript
js浏览器滚动条卷去的高度scrolltop(实例讲解)
2017/07/07 Javascript
基于react组件之间的参数传递(详解)
2017/09/05 Javascript
vue分页器组件编写方法详解
2019/06/28 Javascript
Python简单计算数组元素平均值的方法示例
2017/12/26 Python
python中reader的next用法
2018/07/24 Python
在python中利用opencv简单做图片比对的方法
2019/01/24 Python
python deque模块简单使用代码实例
2020/03/12 Python
Python try except异常捕获机制原理解析
2020/04/18 Python
python爬虫爬取图片的简单代码
2021/01/18 Python
Ubuntu20.04环境安装tensorflow2的方法步骤
2021/01/29 Python
对CSS3选择器的研究(详解)
2016/09/16 HTML / CSS
你的创业计划书怎样才能打动风投
2014/02/06 职场文书
国窖1573广告词
2014/03/21 职场文书
研究生考核个人自我鉴定
2014/03/27 职场文书
开工仪式策划方案
2014/05/23 职场文书
党员自我评议个人对照检查材料
2014/09/16 职场文书
乡镇党的群众路线教育实践活动制度建设计划
2014/11/03 职场文书
党支部承诺书
2015/01/20 职场文书
2015年社区宣传工作总结
2015/05/20 职场文书
《曹冲称象》教学反思
2016/02/20 职场文书
Spring boot应用启动后首次访问很慢的解决方案
2021/06/23 Java/Android