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()。
本站公眾號
   歡迎關注本站公眾號,獲取更多信息