浅谈node的事件机制

作者:donnylin 时间:2024-05-05 09:22:03 

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

来源:http://www.jianshu.com/p/a306c36f5e00?utm_source=tuicool&utm_medium=referral

标签:node,事件机制
0
投稿

猜你喜欢

  • python算法深入理解风控中的KS原理

    2021-01-12 21:27:37
  • Python基础之函数基本用法与进阶详解

    2023-07-13 07:57:46
  • Python学习pygal绘制线图代码分享

    2022-12-02 10:51:48
  • Jupyter Notebook 如何修改字体和大小以及更改字体样式

    2022-01-20 00:21:44
  • 详解Python3 对象组合zip()和回退方式*zip

    2021-03-01 11:14:09
  • Python检查和同步本地时间(北京时间)的实现方法

    2022-02-23 04:42:41
  • python中如何写类

    2023-09-02 18:19:58
  • golang分层测试之http接口测试入门教程

    2024-05-25 15:12:00
  • Python 错误和异常小结

    2021-08-19 12:17:58
  • python selenium在打开的浏览器中动态调整User Agent

    2022-09-26 13:41:59
  • MySQL利用索引优化ORDER BY排序语句的方法

    2024-01-24 00:19:34
  • 怎么让别人看不到网页源代码

    2008-03-21 13:06:00
  • 使用python在本地电脑上快速处理数据

    2022-07-30 14:43:11
  • Python 如何对文件目录操作

    2022-10-08 05:40:04
  • 省市级联菜单的可用性

    2009-02-24 16:32:00
  • Golang使用lua脚本实现redis原子操作

    2023-09-03 05:55:20
  • firefox与ie 的javascript区别

    2010-03-14 11:30:00
  • flask框架视图函数用法示例

    2022-07-18 09:36:18
  • 关于超级链接的一些问题

    2007-12-07 14:00:00
  • Python中反转二维数组的行和列问题

    2021-06-24 13:48:27
  • asp之家 网络编程 m.aspxhome.com