91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Nodejs v14源碼分析之如何使用Event模塊

發布時間:2021-10-19 09:26:54 來源:億速云 閱讀:157 作者:iii 欄目:web開發

本篇內容主要講解“Nodejs v14源碼分析之如何使用Event模塊”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Nodejs v14源碼分析之如何使用Event模塊”吧!

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

const { EventEmitter } = require('events');   class Events extends EventEmitter {}   const events = new Events();   events.on('demo', () => {       console.log('emit demo event');   });   events.emit('demo');

接下來我們看一下events模塊的具體實現。

1 初始化 當new一個EventEmitter或者它的子類時,就會進入EventEmitter的邏輯。

function EventEmitter(opts) {     EventEmitter.init.call(this, opts);   }      EventEmitter.init = function(opts) {     // 如果是未初始化或者沒有自定義_events,則初始化     if (this._events === undefined ||         this._events === ObjectGetPrototypeOf(this)._events) {        this._events = ObjectCreate(null);        this._eventsCount = 0;      }      /*      初始化一類事件的處理函數個數的閾值      我們可以通過setMaxListeners接口設置,      如果沒有顯示設置,閾值則為defaultMaxListeners的值(10),      可通過getMaxListeners接口獲取    */      this._maxListeners = this._maxListeners || undefined;          // 是否開啟捕獲promise reject,默認false      if (opts && opts.captureRejections) {        this[kCapture] = Boolean(opts.captureRejections);      } else {        this[kCapture] = EventEmitter.prototype[kCapture];      }    };

EventEmitter的初始化主要是初始化了一些數據結構和屬性。唯一支持的一個參數就是captureRejections,captureRejections表示當觸發事件,執行處理函數時,EventEmitter是否捕獲處理函數中的異常。后面我們會詳細講解。

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

function _addListener(target, type, listener, prepend) {     let m;     let events;     let existing;     events = target._events;     // 還沒有初始化_events則初始化,_eventsCount為事件類型個數     if (events === undefined) {       events = target._events = ObjectCreate(null);       target._eventsCount = 0;      } else {        /*         已經注冊過事件,則判斷是否定義了newListener事件,         是的話先觸發,如果監聽了newListener事件,每次注冊         其它事件時都會觸發newListener,相當于鉤子       */        if (events.newListener !== undefined) {          target.emit('newListener',                       type,                      listener.listener ?                       listener.listener :                       listener);          // newListener處理函數可能會修改_events,這里重新賦值          events = target._events;        }        // 判斷是否已經存在處理函數        existing = events[type];      }      // 不存在則以函數的形式存儲,否則以數組形式存儲      if (existing === undefined) {        events[type] = listener;        // 新增一個事件類型,事件類型個數加一      ++target._eventsCount;      } else {        /*          existing是函數說明之前注冊過該事件一次,         否則說明existing為數組,則直接插入相應位置      */      if (typeof existing === 'function') {          existing = events[type] =            prepend ? [listener, existing] : [existing, listener];        } else if (prepend) {          existing.unshift(listener);        } else {          existing.push(listener);        }            // 處理告警,處理函數過多可能是因為之前的沒有刪除,造成內存泄漏        m = _getMaxListeners(target);        // 該事件處理函數達到閾值并且還沒有提示過警告信息則提示      if (m > 0 && existing.length > m && !existing.warned) {          existing.warned = true;          const w = new Error('錯誤信息…');          w.name = 'MaxListenersExceededWarning';          w.emitter = target;          w.type = type;          w.count = existing.length;          process.emitWarning(w);        }      }          return target;    }

接下來我們看一下once的實現,對比其它幾種api,once的實現相對比較復雜,因為我們要控制處理函數最多執行一次,所以我們需要保證在事件觸發的時候,執行用戶定義函數的同時,還需要刪除注冊的事件。

ventEmitter.prototype.once = function once(type, listener) {    this.on(type, _onceWrap(this, type, listener));    return this;   ;     unction onceWrapper() {    // 還沒有觸發過    if (!this.fired) {      // 刪除它       this.target.removeListener(this.type, this.wrapFn);       // 觸發了       this.fired = true;       // 執行       if (arguments.length === 0)         return this.listener.call(this.target);       return this.listener.apply(this.target, arguments);     }   }   // 支持once api   function _onceWrap(target, type, listener) {     // fired是否已執行處理函數,wrapFn包裹listener的函數     const state = { fired: false, wrapFn: undefined, target, type, listener };     // 生成一個包裹listener的函數     const wrapped = onceWrapper.bind(state);     /*     把原函數listener也掛到包裹函數中,用于事件沒有觸發前,     用戶主動刪除,見removeListener     */   wrapped.listener = listener;     // 保存包裹函數,用于執行完后刪除,見onceWrapper     state.wrapFn = wrapped;     return wrapped;   }

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

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

EventEmitter.prototype.emit = function emit(type, ...args) {     // 觸發的事件是否是error,error事件需要特殊處理     let doError = (type === 'error');        const events = this._events;     // 定義了處理函數(不一定是type事件的處理函數)     if (events !== undefined) {        /*       如果觸發的事件是error,并且監聽了kErrorMonitor       事件則觸發kErrorMonitor事件      */        if (doError && events[kErrorMonitor] !== undefined)          this.emit(kErrorMonitor, ...args);        // 觸發的是error事件但是沒有定義處理函數        doError = (doError && events.error === undefined);      } else if (!doError)       // 沒有定義處理函數并且觸發的不是error事件則不需要處理,        return false;          // If there is no 'error' event listener then throw.      // 觸發的是error事件,但是沒有定義處理error事件的函數,則報錯      if (doError) {        let er;        if (args.length > 0)          er = args[0];        // 第一個入參是Error的實例        if (er instanceof Error) {          try {            const capture = {};            /*             給capture對象注入stack屬性,stack的值是執行                 Error.captureStackTrace語句的當前棧信息,但是             不包括emit的部分           */            Error.captureStackTrace(capture, EventEmitter.prototype.emit);            ObjectDefineProperty(er, kEnhanceStackBeforeInspector, {              value: enhanceStackTrace.bind(this, er, capture),              configurable: true            });          } catch {}          throw er; // Unhandled 'error' event        }            let stringifiedEr;        const { inspect } = require('internal/util/inspect');        try {          stringifiedEr = inspect(er);        } catch {          stringifiedEr = er;        }        const err = new ERR_UNHANDLED_ERROR(stringifiedEr);        err.context = er;        throw err; // Unhandled 'error' event      }      // 獲取type事件對應的處理函數      const handler = events[type];      // 沒有則不處理      if (handler === undefined)        return false;      // 等于函數說明只有一個      if (typeof handler === 'function') {        // 直接執行        const result = ReflectApply(handler, this, args);        // 非空判斷是不是promise并且是否需要處理,見addCatch        if (result !== undefined && result !== null) {          addCatch(this, result, type, args);        }      } else {        // 多個處理函數,同上        const len = handler.length;        const listeners = arrayClone(handler, len);        for (let i = 0; i < len; ++i) {          const result = ReflectApply(listeners[i], this, args);          if (result !== undefined && result !== null) {            addCatch(this, result, type, args);          }        }      }          return true;    }

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

function addCatch(that, promise, type, args) {     // 沒有開啟捕獲則不需要處理     if (!that[kCapture]) {       return;     }     // that throws on second use.     try {       const then = promise.then;           if (typeof then === 'function') {          // 注冊reject的處理函數          then.call(promise, undefined, function(err) {            process.nextTick(emitUnhandledRejectionOrErr, that, err, type, args);          });        }      } catch (err) {        that.emit('error', err);      }    }        function emitUnhandledRejectionOrErr(ee, err, type, args) {      // 用戶實現了kRejection則執行      if (typeof ee[kRejection] === 'function') {        ee[kRejection](err, type, ...args);      } else {        // 保存當前值        const prev = ee[kCapture];        try {          /*           關閉然后觸發error事件,意義           1 防止error事件處理函數也拋出error,導致死循環           2 如果用戶處理了error,則進程不會退出,所以需要恢復             kCapture的值如果用戶沒有處理error,則Node.js會觸發             uncaughtException,如果用戶處理了uncaughtException             則需要恢復kCapture的值         */          ee[kCapture] = false;          ee.emit('error', err);        } finally {          ee[kCapture] = prev;        }      }    }

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

function removeAllListeners(type) {         const events = this._events;         if (events === undefined)           return this;            /*         沒有注冊removeListener事件,則只需要刪除數據,         否則還需要觸發removeListener事件          */        if (events.removeListener === undefined) {            // 等于0說明是刪除全部            if (arguments.length === 0) {              this._events = ObjectCreate(null);              this._eventsCount = 0;            } else if (events[type] !== undefined) {              /*               否則是刪除某個類型的事件,是唯一一個處理函數,               則重置_events,否則刪除對應的事件類型                      */            if (--this._eventsCount === 0)                this._events = ObjectCreate(null);              else                delete events[type];            }            return this;          }              /*          說明注冊了removeListener事件,arguments.length === 0          說明刪除所有類型的事件           */        if (arguments.length === 0) {            /*             逐個刪除,除了removeListener事件,            這里刪除了非removeListener事件           */            for (const key of ObjectKeys(events)) {              if (key === 'removeListener') continue;              this.removeAllListeners(key);            }            // 這里刪除removeListener事件,見下面的邏輯            this.removeAllListeners('removeListener');            // 重置數據結構            this._events = ObjectCreate(null);            this._eventsCount = 0;            return this;          }          // 刪除某類型事件          const listeners = events[type];              if (typeof listeners === 'function') {            this.removeListener(type, listeners);          } else if (listeners !== undefined) {            // LIFO order            for (let i = listeners.length - 1; i >= 0; i--) {              this.removeListener(type, listeners[i]);            }          }              return this;        }

removeAllListeners函數主要的邏輯有兩點,第一個是removeListener事件需要特殊處理,這類似一個鉤子,每次用戶刪除事件處理函數的時候都會觸發該事件。第二是removeListener函數。removeListener是真正刪除事件處理函數的實現。removeAllListeners是封裝了removeListener的邏輯。

function removeListener(type, listener) {      let originalListener;      const events = this._events;      // 沒有東西可刪除      if (events === undefined)        return this;         const list = events[type];      // 同上       if (list === undefined)         return this;       // list是函數說明只有一個處理函數,否則是數組,如果list.listener === listener說明是once注冊的       if (list === listener || list.listener === listener) {         // type類型的處理函數就一個,并且也沒有注冊其它類型的事件,則初始化_events         if (--this._eventsCount === 0)           this._events = ObjectCreate(null);         else {           // 就一個執行完刪除type對應的屬性           delete events[type];           // 注冊了removeListener事件,則先注冊removeListener事件           if (events.removeListener)             this.emit('removeListener',                       type,                       list.listener || listener);         }       } else if (typeof list !== 'function') {         // 多個處理函數         let position = -1;         // 找出需要刪除的函數         for (let i = list.length - 1; i >= 0; i--) {           if (list[i] === listener ||               list[i].listener === listener) {             // 保存原處理函數,如果有的話             originalListener = list[i].listener;             position = i;             break;           }         }             if (position < 0)           return this;         // 第一個則出隊,否則刪除一個         if (position === 0)           list.shift();         else {           if (spliceOne === undefined)             spliceOne = require('internal/util').spliceOne;           spliceOne(list, position);         }         // 如果只剩下一個,則值改成函數類型         if (list.length === 1)           events[type] = list[0];         // 觸發removeListener         if (events.removeListener !== undefined)           this.emit('removeListener',                      type,                     originalListener || listener);       }           return this;     };

到此,相信大家對“Nodejs v14源碼分析之如何使用Event模塊”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

墨脱县| 兰州市| 海城市| 临澧县| 长垣县| 乐清市| 青海省| 诏安县| 普格县| 迁安市| 宜阳县| 彭阳县| 金湖县| 东城区| 黄冈市| 台江县| 油尖旺区| 花莲县| 盖州市| 江北区| 娄烦县| 林州市| 宝丰县| 荣成市| 义马市| 古田县| 七台河市| 芦溪县| 宿松县| 巫溪县| 惠安县| 泗洪县| 梨树县| 凌海市| 邵阳县| 阜南县| 大兴区| 金川县| 大庆市| 两当县| 东山县|