MetaMask/safe-event-emitter

https://github.com/MetaMask/safe-event-emitter

 

safe-event-emitter

An EventEmitter that isolates the emitter from errors in handlers. If an error is thrown in a handler it is caught and re-thrown inside of a setTimeout so as to not interupt the emitter's code flow.

是一个emitter与处理程序handlers中的错误隔离的事件发射器。如果在处理程序中抛出错误,就会被捕获并在setTimeout中重新抛出,以便不会中断发射器的代码流

API is the same as EventEmitter.

usage

const SafeEventEmitter = require('safe-event-emitter')

const ee = new SafeEventEmitter()
ee.on('boom', () => { throw new Error() })
ee.emit('boom') // no error here

// error is thrown after setTimeout

 

safe-event-emitter/index.js

const util = require('util')
const EventEmitter = require('events/')

var R = typeof Reflect === 'object' ? Reflect : null    //Reflect是对象则为R,否则R为null
var ReflectApply = R && typeof R.apply === 'function'  //当R不为null且R.apply是函数时,ReflectApply为R.apply函数,否则就是定义的ReflectApply函数
  ? R.apply
  : function ReflectApply(target, receiver, args) {
    return Function.prototype.apply.call(target, receiver, args);
}

module.exports = SafeEventEmitter


function SafeEventEmitter() {
  EventEmitter.call(this)
}

util.inherits(SafeEventEmitter, EventEmitter)

SafeEventEmitter.prototype.emit = function (type) {//事件触发函数
  // copied from https://github.com/Gozala/events/blob/master/events.js
  // modified lines are commented with "edited:"
  var args = [];
  for (var i = 1; i < arguments.length; i++) args.push(arguments[i]); //将传入的参数push进数组args
  var doError = (type === 'error'); //即事件的类型是否为“error”

  var events = this._events;
  if (events !== undefined)//当事件被定义了时,即调用过on()进行监听
    doError = (doError && events.error === undefined); //事件为error但是events.error没有定义doError才返回true
  else if (!doError) //如果不是error事件则返回false
    return false;

  // If there is no 'error' event listener then throw.
  if (doError) { //如果为error事件
    var er;
    if (args.length > 0) //且有参数传入
      er = args[0]; //第一个参数应该为Error
    if (er instanceof Error) {//name就将错误抛出
      // Note: The comments on the `throw` lines are intentional, they show
      // up in Node's output if this results in an unhandled exception.
      throw er; // Unhandled 'error' event
    }
    // At least give some kind of context to the user 否则就说明没有定义Error,那么下面就会定义相应的错误抛出
    var err = new Error('Unhandled error.' + (er ? ' (' + er.message + ')' : ''));
    err.context = er;
    throw err; // Unhandled 'error' event
  }

  var handler = events[type];//得到on()监听时定义的回调函数

  if (handler === undefined)//如果回调没定义则返回false
    return false;

  if (typeof handler === 'function') {//如果定义了则调用下面的safeApply(回调函数,上下文环境,参数)
    // edited: using safeApply
    safeApply(handler, this, args);
  } else { //如果回调不是一个函数,而是一个函数数组,则调用arrayClone()
    var len = handler.length;
    var listeners = arrayClone(handler, len);
    for (var i = 0; i < len; ++i)
      // edited: using safeApply
      safeApply(listeners[i], this, args);
  }

  return true;
}

function safeApply(handler, context, args) {//就是在context这个上下文环境中,将相应的args参数传给handler回调函数,对回调函数进行调用
  try {
    ReflectApply(handler, context, args)
  } catch (err) { //特别之处就在于在调用回调过程中如果出错了,错误先使用setTimeout压入堆栈,在整个回调函数调用完成后才抛出
    // throw error after timeout so as not to interupt the stack
    setTimeout(() => {
      throw err
    })
  }
}

function arrayClone(arr, n) {
  var copy = new Array(n);
  for (var i = 0; i < n; ++i)
    copy[i] = arr[i];
  return copy;
}

 

补充知识:

Reflect 是一个内置的对象,它提供拦截 JavaScript 操作的方法。这些方法与处理器对象的方法相同。Reflect不是一个函数对象,因此它是不可构造的。

与大多数全局对象不同,Reflect没有构造函数。你不能将其与一个new运算符一起使用,或者将Reflect对象作为一个函数来调用。Reflect的所有属性和方法都是静态的(就像Math对象)。

 

可以理解为:有这么一个全局对象,上面直接挂载了对象的某些特殊方法,这些方法可以通过Reflect.apply这种形式来使用,当然所有方法都是可以在 Object 的原型链中找到的。

使用reflect的好处

引自知乎专栏:ES6 Reflect

Reflect上面的一些方法并不是专门为对象设计的,比如Reflect.apply方法,它的参数是一个函数,如果使用Object.apply(func)会让人感觉很奇怪。
用一个单一的全局对象去存储这些方法,能够保持其它的JavaScript代码的整洁、干净。不然的话,这些方法可能是全局的,或者要通过原型来调用。
将一些命令式的操作如delete,in等使用函数来替代,这样做的目的是为了让代码更加好维护,更容易向下兼容;也避免出现更多的保留字。

方法:

Reflect对象提供以下静态函数,它们具有与处理器对象方法相同的名称。这些方法中的一些与 Object 上的对应方法相同。

Reflect.apply()                        对一个函数进行调用操作,同时可以传入一个数组作为调用参数。和 Function.prototype.apply() 功能类似。
Reflect.construct()                    对构造函数进行 new 操作,相当于执行 new target(...args)。
Reflect.defineProperty()               和 Object.defineProperty() 类似。
Reflect.deleteProperty()               作为函数的delete操作符,相当于执行 delete target[name]。
Reflect.enumerate()                    该方法会返回一个包含有目标对象身上所有可枚举的自身字符串属性以及继承字符串属性的迭代器,for...in 操作遍历到的正是这些属性。
Reflect.get()                          获取对象身上某个属性的值,类似于 target[name]。
Reflect.getOwnPropertyDescriptor()     类似于 Object.getOwnPropertyDescriptor()。
Reflect.getPrototypeOf()               类似于 Object.getPrototypeOf()。
Reflect.has()                          判断一个对象是否存在某个属性,和 in 运算符 的功能完全相同。
Reflect.isExtensible()                 类似于 Object.isExtensible().
Reflect.ownKeys()                      返回一个包含所有自身属性(不包含继承属性)的数组。
Reflect.preventExtensions()            类似于 Object.preventExtensions()。返回一个Boolean。
Reflect.set()                          将值分配给属性的函数。返回一个Boolean,如果更新成功,则返回true。
Reflect.setPrototypeOf()               类似于 Object.setPrototypeOf()。

 

 

 
posted @ 2018-11-07 16:52  慢行厚积  阅读(556)  评论(0编辑  收藏  举报