自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Nodejs v14源碼分析之Event模塊

開發(fā) 前端
events模塊是Node.js中比較簡單但是卻非常核心的模塊,Node.js中,很多模塊都繼承于events模塊,events模塊是發(fā)布、訂閱模式的實(shí)現(xiàn)。我們首先看一下如何使用events模塊。

[[381910]]

本文轉(zhuǎn)載自微信公眾號「編程雜技」,作者theanarkh。轉(zhuǎn)載本文請聯(lián)系編程雜技公眾號。     

events模塊是Node.js中比較簡單但是卻非常核心的模塊,Node.js中,很多模塊都繼承于events模塊,events模塊是發(fā)布、訂閱模式的實(shí)現(xiàn)。我們首先看一下如何使用events模塊。

  1. const { EventEmitter } = require('events');   
  2. class Events extends EventEmitter {}   
  3. const events = new Events();   
  4. events.on('demo', () => {   
  5.     console.log('emit demo event');   
  6. });   
  7. events.emit('demo'); 

接下來我們看一下events模塊的具體實(shí)現(xiàn)。

1 初始化 當(dāng)new一個(gè)EventEmitter或者它的子類時(shí),就會進(jìn)入EventEmitter的邏輯。

  1. function EventEmitter(opts) {   
  2.   EventEmitter.init.call(this, opts);   
  3. }   
  4.    
  5. EventEmitter.init = function(opts) {   
  6.   // 如果是未初始化或者沒有自定義_events,則初始化   
  7.   if (this._events === undefined ||   
  8.       this._events === ObjectGetPrototypeOf(this)._events) {   
  9.      this._events = ObjectCreate(null);   
  10.      this._eventsCount = 0;   
  11.    }   
  12.    /* 
  13.      初始化一類事件的處理函數(shù)個(gè)數(shù)的閾值 
  14.      我們可以通過setMaxListeners接口設(shè)置, 
  15.      如果沒有顯示設(shè)置,閾值則為defaultMaxListeners的值(10), 
  16.      可通過getMaxListeners接口獲取 
  17.    */   
  18.    this._maxListeners = this._maxListeners || undefined;   
  19.     
  20.    // 是否開啟捕獲promise reject,默認(rèn)false   
  21.    if (opts && opts.captureRejections) {   
  22.      this[kCapture] = Boolean(opts.captureRejections);   
  23.    } else {   
  24.      this[kCapture] = EventEmitter.prototype[kCapture];   
  25.    }   
  26.  }; 

EventEmitter的初始化主要是初始化了一些數(shù)據(jù)結(jié)構(gòu)和屬性。唯一支持的一個(gè)參數(shù)就是captureRejections,captureRejections表示當(dāng)觸發(fā)事件,執(zhí)行處理函數(shù)時(shí),EventEmitter是否捕獲處理函數(shù)中的異常。后面我們會詳細(xì)講解。

2 訂閱事件 初始化完EventEmitter之后,我們就可以開始使用訂閱、發(fā)布的功能。我們可以通過addListener、prependListener、on、once訂閱事件。addListener和on是等價(jià)的,prependListener的區(qū)別在于處理函數(shù)會被插入到隊(duì)首,而默認(rèn)是追加到隊(duì)尾。once注冊的處理函數(shù),最多被執(zhí)行一次。四個(gè)api都是通過_addListener函數(shù)實(shí)現(xiàn)的。下面我們看一下具體實(shí)現(xiàn)。

  1. function _addListener(target, type, listener, prepend) {   
  2.   let m;   
  3.   let events;   
  4.   let existing;   
  5.   events = target._events;   
  6.   // 還沒有初始化_events則初始化,_eventsCount為事件類型個(gè)數(shù)   
  7.   if (events === undefined) {   
  8.     events = target._events = ObjectCreate(null);   
  9.     target._eventsCount = 0;   
  10.    } else {   
  11.      /*  
  12.        已經(jīng)注冊過事件,則判斷是否定義了newListener事件, 
  13.         是的話先觸發(fā),如果監(jiān)聽了newListener事件,每次注冊 
  14.         其它事件時(shí)都會觸發(fā)newListener,相當(dāng)于鉤子  
  15.      */   
  16.      if (events.newListener !== undefined) {   
  17.        target.emit('newListener',  
  18.                      type,   
  19.                    listener.listener ?  
  20.                      listener.listener :  
  21.                      listener);   
  22.        // newListener處理函數(shù)可能會修改_events,這里重新賦值   
  23.        events = target._events;   
  24.      }   
  25.      // 判斷是否已經(jīng)存在處理函數(shù)   
  26.      existing = events[type];   
  27.    }   
  28.    // 不存在則以函數(shù)的形式存儲,否則以數(shù)組形式存儲   
  29.    if (existing === undefined) {   
  30.      events[type] = listener;   
  31.      // 新增一個(gè)事件類型,事件類型個(gè)數(shù)加一 
  32.      ++target._eventsCount;   
  33.    } else {   
  34.      /*  
  35.         existing是函數(shù)說明之前注冊過該事件一次, 
  36.         否則說明existing為數(shù)組,則直接插入相應(yīng)位置 
  37.      */ 
  38.      if (typeof existing === 'function') {   
  39.        existing = events[type] =   
  40.          prepend ? [listener, existing] : [existing, listener];   
  41.      } else if (prepend) {   
  42.        existing.unshift(listener);   
  43.      } else {   
  44.        existing.push(listener);   
  45.      }   
  46.     
  47.      // 處理告警,處理函數(shù)過多可能是因?yàn)橹暗臎]有刪除,造成內(nèi)存泄漏   
  48.      m = _getMaxListeners(target);   
  49.      // 該事件處理函數(shù)達(dá)到閾值并且還沒有提示過警告信息則提示 
  50.      if (m > 0 && existing.length > m && !existing.warned) {   
  51.        existing.warned = true;   
  52.        const w = new Error('錯(cuò)誤信息…');   
  53.        w.name = 'MaxListenersExceededWarning';   
  54.        w.emitter = target;   
  55.        w.type = type;   
  56.        w.count = existing.length;   
  57.        process.emitWarning(w);   
  58.      }   
  59.    }   
  60.     
  61.    return target;   
  62.  } 

接下來我們看一下once的實(shí)現(xiàn),對比其它幾種api,once的實(shí)現(xiàn)相對比較復(fù)雜,因?yàn)槲覀円刂铺幚砗瘮?shù)最多執(zhí)行一次,所以我們需要保證在事件觸發(fā)的時(shí)候,執(zhí)行用戶定義函數(shù)的同時(shí),還需要?jiǎng)h除注冊的事件。

  1. ventEmitter.prototype.once = function once(type, listener) {   
  2.  this.on(type, _onceWrap(this, type, listener));   
  3.  return this;   
  4. ;   
  5.   
  6. unction onceWrapper() {   
  7.  // 還沒有觸發(fā)過   
  8.  if (!this.fired) {   
  9.    // 刪除它   
  10.     this.target.removeListener(this.type, this.wrapFn);   
  11.     // 觸發(fā)了   
  12.     this.fired = true;   
  13.     // 執(zhí)行   
  14.     if (arguments.length === 0)   
  15.       return this.listener.call(this.target);   
  16.     return this.listener.apply(this.target, arguments);   
  17.   }   
  18. }   
  19. // 支持once api   
  20. function _onceWrap(target, type, listener) {   
  21.   // fired是否已執(zhí)行處理函數(shù),wrapFn包裹listener的函數(shù)   
  22.   const state = { fired: false, wrapFn: undefined, target, type, listener };   
  23.   // 生成一個(gè)包裹listener的函數(shù)   
  24.   const wrapped = onceWrapper.bind(state);   
  25.   /* 
  26.     把原函數(shù)listener也掛到包裹函數(shù)中,用于事件沒有觸發(fā)前, 
  27.     用戶主動(dòng)刪除,見removeListener   
  28.   */ 
  29.   wrapped.listener = listener;   
  30.   // 保存包裹函數(shù),用于執(zhí)行完后刪除,見onceWrapper   
  31.   state.wrapFn = wrapped;   
  32.   return wrapped;   

Once函數(shù)構(gòu)造一個(gè)上下文(state)保存用戶處理函數(shù)和執(zhí)行狀態(tài)等信息,然后通過bind返回一個(gè)帶有該上下文(state)的函數(shù)wrapped注冊到事件系統(tǒng)。當(dāng)事件觸發(fā)時(shí),在wrapped函數(shù)中首先移除wrapped,然后執(zhí)行用戶的函數(shù)。Wrapped起到了劫持的作用。另外還需要在wrapped上保存用戶傳進(jìn)來的函數(shù),當(dāng)用戶在事件觸發(fā)前刪除該事件時(shí)或解除該函數(shù)時(shí),在遍歷該類事件的處理函數(shù)過程中,可以通過wrapped.listener找到對應(yīng)的項(xiàng)進(jìn)行刪除。

3 觸發(fā)事件 分析完事件的訂閱,接著我們看一下事件的觸發(fā)。

  1. EventEmitter.prototype.emit = function emit(type, ...args) {   
  2.   // 觸發(fā)的事件是否是error,error事件需要特殊處理   
  3.   let doError = (type === 'error');   
  4.    
  5.   const events = this._events;   
  6.   // 定義了處理函數(shù)(不一定是type事件的處理函數(shù))   
  7.   if (events !== undefined) {   
  8.      /* 
  9.       如果觸發(fā)的事件是error,并且監(jiān)聽了kErrorMonitor 
  10.       事件則觸發(fā)kErrorMonitor事件 
  11.      */   
  12.      if (doError && events[kErrorMonitor] !== undefined)   
  13.        this.emit(kErrorMonitor, ...args);   
  14.      // 觸發(fā)的是error事件但是沒有定義處理函數(shù)   
  15.      doError = (doError && events.error === undefined);   
  16.    } else if (!doError)  
  17.      // 沒有定義處理函數(shù)并且觸發(fā)的不是error事件則不需要處理,   
  18.      return false;   
  19.     
  20.    // If there is no 'error' event listener then throw.   
  21.    // 觸發(fā)的是error事件,但是沒有定義處理error事件的函數(shù),則報(bào)錯(cuò)   
  22.    if (doError) {   
  23.      let er;   
  24.      if (args.length > 0)   
  25.        er = args[0];   
  26.      // 第一個(gè)入?yún)⑹荅rror的實(shí)例   
  27.      if (er instanceof Error) {   
  28.        try {   
  29.          const capture = {};   
  30.          /*  
  31.            給capture對象注入stack屬性,stack的值是執(zhí)行     
  32.             Error.captureStackTrace語句的當(dāng)前棧信息,但是 
  33.             不包括emit的部分  
  34.          */   
  35.          Error.captureStackTrace(capture, EventEmitter.prototype.emit);   
  36.          ObjectDefineProperty(er, kEnhanceStackBeforeInspector, {   
  37.            value: enhanceStackTrace.bind(this, er, capture),   
  38.            configurable: true   
  39.          });   
  40.        } catch {}   
  41.        throw er; // Unhandled 'error' event   
  42.      }   
  43.     
  44.      let stringifiedEr;   
  45.      const { inspect } = require('internal/util/inspect');   
  46.      try {   
  47.        stringifiedEr = inspect(er);   
  48.      } catch {   
  49.        stringifiedEr = er;   
  50.      }   
  51.      const err = new ERR_UNHANDLED_ERROR(stringifiedEr);   
  52.      err.context = er;   
  53.      throw err; // Unhandled 'error' event   
  54.    }   
  55.    // 獲取type事件對應(yīng)的處理函數(shù)   
  56.    const handler = events[type];   
  57.    // 沒有則不處理   
  58.    if (handler === undefined)   
  59.      return false;   
  60.    // 等于函數(shù)說明只有一個(gè)   
  61.    if (typeof handler === 'function') {   
  62.      // 直接執(zhí)行   
  63.      const result = ReflectApply(handler, this, args);   
  64.      // 非空判斷是不是promise并且是否需要處理,見addCatch   
  65.      if (result !== undefined && result !== null) {   
  66.        addCatch(this, result, type, args);   
  67.      }   
  68.    } else {   
  69.      // 多個(gè)處理函數(shù),同上   
  70.      const len = handler.length;   
  71.      const listeners = arrayClone(handler, len);   
  72.      for (let i = 0; i < len; ++i) {   
  73.        const result = ReflectApply(listeners[i], this, args);   
  74.        if (result !== undefined && result !== null) {   
  75.          addCatch(this, result, type, args);   
  76.        }   
  77.      }   
  78.    }   
  79.     
  80.    return true;   
  81.  } 

我們看到在Node.js中,對于error事件是特殊處理的,如果用戶沒有注冊error事件的處理函數(shù),可能會導(dǎo)致程序掛掉,另外我們看到有一個(gè)addCatch的邏輯,addCatch是為了支持事件處理函數(shù)為異步模式的情況,比如async函數(shù)或者返回Promise的函數(shù)。

  1. function addCatch(that, promise, type, args) {   
  2.   // 沒有開啟捕獲則不需要處理   
  3.   if (!that[kCapture]) {   
  4.     return;   
  5.   }   
  6.   // that throws on second use.   
  7.   try {   
  8.     const then = promise.then;   
  9.    
  10.      if (typeof then === 'function') {   
  11.        // 注冊reject的處理函數(shù)   
  12.        then.call(promise, undefined, function(err) {   
  13.          process.nextTick(emitUnhandledRejectionOrErr, that, err, type, args);   
  14.        });   
  15.      }   
  16.    } catch (err) {   
  17.      that.emit('error', err);   
  18.    }   
  19.  }   
  20.     
  21.  function emitUnhandledRejectionOrErr(ee, err, type, args) {   
  22.    // 用戶實(shí)現(xiàn)了kRejection則執(zhí)行   
  23.    if (typeof ee[kRejection] === 'function') {   
  24.      ee[kRejection](err, type, ...args);   
  25.    } else {   
  26.      // 保存當(dāng)前值   
  27.      const prev = ee[kCapture];   
  28.      try {   
  29.        /*  
  30.          關(guān)閉然后觸發(fā)error事件,意義  
  31.          1 防止error事件處理函數(shù)也拋出error,導(dǎo)致死循環(huán)  
  32.          2 如果用戶處理了error,則進(jìn)程不會退出,所以需要恢復(fù) 
  33.             kCapture的值如果用戶沒有處理error,則Node.js會觸發(fā) 
  34.             uncaughtException,如果用戶處理了uncaughtException 
  35.             則需要恢復(fù)kCapture的值  
  36.        */   
  37.        ee[kCapture] = false;   
  38.        ee.emit('error', err);   
  39.      } finally {   
  40.        ee[kCapture] = prev;   
  41.      }   
  42.    }   
  43.  } 

4 取消訂閱 我們接著看一下刪除事件處理函數(shù)的邏輯。

  1. function removeAllListeners(type) {   
  2.       const events = this._events;   
  3.       if (events === undefined)   
  4.         return this;   
  5.    
  6.       /* 
  7.         沒有注冊removeListener事件,則只需要?jiǎng)h除數(shù)據(jù), 
  8.         否則還需要觸發(fā)removeListener事件   
  9.        */ 
  10.        if (events.removeListener === undefined) {   
  11.          // 等于0說明是刪除全部   
  12.          if (arguments.length === 0) {   
  13.            this._events = ObjectCreate(null);   
  14.            this._eventsCount = 0;   
  15.          } else if (events[type] !== undefined) {  
  16.             /* 
  17.               否則是刪除某個(gè)類型的事件,是唯一一個(gè)處理函數(shù), 
  18.               則重置_events,否則刪除對應(yīng)的事件類型          
  19.             */ 
  20.            if (--this._eventsCount === 0)   
  21.              this._events = ObjectCreate(null);   
  22.            else   
  23.              delete events[type];   
  24.          }   
  25.          return this;   
  26.        }   
  27.     
  28.        /* 
  29.          說明注冊了removeListener事件,arguments.length === 0 
  30.          說明刪除所有類型的事件   
  31.         */ 
  32.        if (arguments.length === 0) {   
  33.          /*  
  34.            逐個(gè)刪除,除了removeListener事件, 
  35.            這里刪除了非removeListener事件 
  36.           */   
  37.          for (const key of ObjectKeys(events)) {   
  38.            if (key === 'removeListener'continue;   
  39.            this.removeAllListeners(key);   
  40.          }   
  41.          // 這里刪除removeListener事件,見下面的邏輯   
  42.          this.removeAllListeners('removeListener');   
  43.          // 重置數(shù)據(jù)結(jié)構(gòu)   
  44.          this._events = ObjectCreate(null);   
  45.          this._eventsCount = 0;   
  46.          return this;   
  47.        }   
  48.        // 刪除某類型事件   
  49.        const listeners = events[type];   
  50.     
  51.        if (typeof listeners === 'function') {   
  52.          this.removeListener(type, listeners);   
  53.        } else if (listeners !== undefined) {   
  54.          // LIFO order   
  55.          for (let i = listeners.length - 1; i >= 0; i--) {   
  56.            this.removeListener(type, listeners[i]);   
  57.          }   
  58.        }   
  59.     
  60.        return this;   
  61.      } 

removeAllListeners函數(shù)主要的邏輯有兩點(diǎn),第一個(gè)是removeListener事件需要特殊處理,這類似一個(gè)鉤子,每次用戶刪除事件處理函數(shù)的時(shí)候都會觸發(fā)該事件。第二是removeListener函數(shù)。removeListener是真正刪除事件處理函數(shù)的實(shí)現(xiàn)。removeAllListeners是封裝了removeListener的邏輯。

  1. function removeListener(type, listener) {   
  2.    let originalListener;   
  3.    const events = this._events;   
  4.    // 沒有東西可刪除   
  5.    if (events === undefined)   
  6.      return this;   
  7.    
  8.    const list = events[type];   
  9.    // 同上   
  10.     if (list === undefined)   
  11.       return this;   
  12.     // list是函數(shù)說明只有一個(gè)處理函數(shù),否則是數(shù)組,如果list.listener === listener說明是once注冊的   
  13.     if (list === listener || list.listener === listener) {   
  14.       // type類型的處理函數(shù)就一個(gè),并且也沒有注冊其它類型的事件,則初始化_events   
  15.       if (--this._eventsCount === 0)   
  16.         this._events = ObjectCreate(null);   
  17.       else {   
  18.         // 就一個(gè)執(zhí)行完刪除type對應(yīng)的屬性   
  19.         delete events[type];   
  20.         // 注冊了removeListener事件,則先注冊removeListener事件   
  21.         if (events.removeListener)   
  22.           this.emit('removeListener'
  23.                       type, 
  24.                       list.listener || listener);   
  25.       }   
  26.     } else if (typeof list !== 'function') {   
  27.       // 多個(gè)處理函數(shù)   
  28.       let position = -1;   
  29.       // 找出需要?jiǎng)h除的函數(shù)   
  30.       for (let i = list.length - 1; i >= 0; i--) {   
  31.         if (list[i] === listener ||  
  32.              list[i].listener === listener) {   
  33.           // 保存原處理函數(shù),如果有的話   
  34.           originalListener = list[i].listener;   
  35.           position = i;   
  36.           break;   
  37.         }   
  38.       }   
  39.     
  40.       if (position < 0)   
  41.         return this;   
  42.       // 第一個(gè)則出隊(duì),否則刪除一個(gè)   
  43.       if (position === 0)   
  44.         list.shift();   
  45.       else {   
  46.         if (spliceOne === undefined)   
  47.           spliceOne = require('internal/util').spliceOne;   
  48.         spliceOne(list, position);   
  49.       }   
  50.       // 如果只剩下一個(gè),則值改成函數(shù)類型   
  51.       if (list.length === 1)   
  52.         events[type] = list[0];   
  53.       // 觸發(fā)removeListener   
  54.       if (events.removeListener !== undefined)   
  55.         this.emit('removeListener',  
  56.                     type, 
  57.                     originalListener || listener);   
  58.     }   
  59.     
  60.     return this;   
  61.   }; 

 

以上就是events模塊的核心邏輯,另外還有一些工具函數(shù)就不一一分析。

 

責(zé)任編輯:武曉燕 來源: 編程雜技
相關(guān)推薦

2020-09-10 09:05:29

UDP服務(wù)器Nodejs

2011-05-26 10:05:48

MongoDB

2022-04-13 11:02:12

鴻蒙事件模塊事件Event

2022-04-24 15:07:09

GPS模塊on函數(shù)鴻蒙

2023-02-26 08:42:10

源碼demouseEffect

2012-09-20 10:07:29

Nginx源碼分析Web服務(wù)器

2021-07-06 09:29:38

Cobar源碼AST

2021-03-23 09:17:58

SpringMVCHttpServletJavaEE

2024-06-13 07:55:19

2023-06-19 08:02:40

2011-05-26 16:18:51

Mongodb

2021-06-04 09:57:49

鴻蒙HarmonyOS應(yīng)用

2021-04-13 09:15:16

C++插件Nodejs

2015-11-23 09:50:15

JavaScript模塊化SeaJs

2017-11-07 12:53:28

Android支持庫

2017-04-19 15:32:46

ReactRouter構(gòu)建源碼

2022-01-12 14:45:26

鴻蒙HarmonyOS應(yīng)用

2020-07-28 08:54:39

內(nèi)核通信Netlink

2021-03-02 09:34:41

Nodejs14前端代碼
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號