MetaMask/safe-event-emitter

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

 

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.github

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

API is the same as EventEmitter.app

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.jside

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不是一个函数对象,所以它是不可构造的。ui

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

 

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

使用reflect的好处

引自知乎专栏:ES6 Reflect

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

spa

方法:prototype

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()。
本站公众号
   欢迎关注本站公众号,获取更多信息