浅谈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 相关文章推荐
将HTML自动转为JS代码
Jun 26 Javascript
用js实现预览待上传的本地图片
Mar 15 Javascript
页面加载完毕后滚动条自动滚动一定位置
Feb 20 Javascript
jQuery 获取兄弟元素的几种不错方法
May 23 Javascript
javascript日期计算实例分析
Jun 29 Javascript
JavaScript精炼之构造函数 Constructor及Constructor属性详解
Nov 05 Javascript
jQuery Easyui快速入门教程
Aug 21 Javascript
Bootstrap table两种分页示例
Dec 23 Javascript
详解vue-router基本使用
Apr 18 Javascript
详解Vue.js分发之作用域槽
Jun 13 Javascript
Vue使用mixin分发组件的可复用功能
Sep 01 Javascript
JS co 函数库的含义和用法实例总结
Apr 08 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
php 判断字符串中是否包含html标签
2014/02/17 PHP
php实现天干地支计算器示例
2014/03/14 PHP
PHP使用array_fill定义多维数组的方法
2015/03/18 PHP
PHP代码优化技巧小结
2015/09/29 PHP
mysql alter table命令修改表结构实例详解
2016/09/24 PHP
js身份证验证超强脚本
2008/10/26 Javascript
理解javascript中的回调函数(callback)
2014/09/02 Javascript
jQuery和AngularJS的区别浅析
2015/01/29 Javascript
jq实现左侧显示图片右侧文字滑动切换效果
2015/08/04 Javascript
jquery获取复选框checkbox的值的简单实现方法
2016/05/26 Javascript
详谈JS中实现种子随机数及作用
2016/07/19 Javascript
用Vue.js实现监听属性的变化
2016/11/17 Javascript
vue-router 手势滑动触发返回功能
2018/09/30 Javascript
Node.js npm命令运行node.js脚本的方法
2018/10/10 Javascript
Vuepress 搭建带评论功能的静态博客的实现
2019/02/17 Javascript
如何在 Vue 表单中处理图片
2021/01/26 Vue.js
django之常用命令详解
2016/06/30 Python
python 通过 socket 发送文件的实例代码
2018/08/14 Python
Python的argparse库使用详解
2018/10/09 Python
python语言基本语句用法总结
2019/06/11 Python
python导入pandas具体步骤方法
2019/06/23 Python
VScode连接远程服务器上的jupyter notebook的实现
2020/04/23 Python
浅谈CSS3鼠标移入图片动态提示效果(transform)
2017/11/06 HTML / CSS
国外平面设计素材网站:The Hungry JPEG
2017/03/28 全球购物
物流管理应届生求职信
2013/11/07 职场文书
采购求职信
2014/03/17 职场文书
书法兴趣小组活动总结
2014/07/07 职场文书
竞选班干部演讲稿600字
2014/08/20 职场文书
学校党的群众路线教育实践活动领导班子对照检查材料
2014/09/25 职场文书
市委常委会班子党的群众路线教育实践活动整改方案
2014/10/25 职场文书
工程部文员岗位职责
2015/02/04 职场文书
计算机实训心得体会
2016/01/14 职场文书
干货:如何写好观后感 !
2019/05/21 职场文书
适合青年人白手起家的创业项目分享
2019/08/16 职场文书
vue3获取当前路由地址
2022/02/18 Vue.js
MySQL查询日期时间
2022/05/15 MySQL