In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article focuses on "Nodejs v14 source code analysis of how to use the Event module", interested friends may wish to have a look. The method introduced in this paper is simple, fast and practical. Next let the editor to take you to learn "Nodejs v14 source code analysis of how to use the Event module"!
Events module is a relatively simple but very core module in Node.js. In Node.js, many modules inherit from events module, and events module is the implementation of publish and subscribe mode. Let's first look at how to use the events module.
Const {EventEmitter} = require ('events'); class Events extends EventEmitter {} const events = new Events (); events.on (' demo', () = > {console.log ('emit demo event');}); events.emit (' demo')
Next, let's take a look at the implementation of the events module.
1 initialization when new an EventEmitter or a subclass of it, it enters the logic of EventEmitter.
Function EventEmitter (opts) {EventEmitter.init.call (this, opts);} EventEmitter.init = function (opts) {/ / if it is uninitialized or does not have a custom _ events, initialize if (this._events = undefined | | this._events = = ObjectGetPrototypeOf (this). _ events) {this._events = ObjectCreate (null); this._eventsCount = 0 } / * the threshold for initializing the number of handling functions for a class of events can be set through the API setMaxListeners. If it is not set, the threshold is the value of defaultMaxListeners (10). You can obtain * / this._maxListeners = this._maxListeners through the API getMaxListeners | | undefined / / whether to enable capture promise reject. Default false if (opts & & opts.captureRejections) {this [kCapture] = Boolean (opts.captureRejections);} else {this [kCapture] = EventEmitter.prototype [kCapture];}}
The initialization of EventEmitter mainly initializes some data structures and attributes. The only parameter supported is captureRejections,captureRejections indicating whether the EventEmitter catches the exception in the handler when the event is triggered and the handler is executed. We will explain it in detail later.
2 after the subscription event initializes the EventEmitter, we can start to use the functions of subscription and publication. We can subscribe to events through addListener, prependListener, on, and once. AddListener and on are equivalent, but the difference with prependListener is that the handler is inserted at the beginning of the queue, while the default is appended to the end of the queue. Handlers registered by once are executed at most once. All four api are implemented through the _ addListener function. Let's take a look at the implementation.
Function _ addListener (target, type, listener, prepend) {let m; let events; let existing; events = target._events; / / initialized before _ events is initialized, and _ eventsCount is the number of event types if (events = = undefined) {events = target._events = ObjectCreate (null); target._eventsCount = 0 } else {/ * has already registered an event, then determine whether the newListener event is defined. If so, trigger it first. If the newListener event is monitored, newListener will be triggered every time another event is registered. Equivalent to hook * / if (events.newListener! = = undefined) {target.emit ('newListener', type, listener.listener? Listener.listener: listener); / / the newListener handler may modify _ events, where the value events = target._events;} / / is reassigned to determine whether the handler function existing = events [type] already exists } / / if it does not exist, it is stored as a function, otherwise if (existing = undefined) {events [type] = listener; / / adds an event type, plus one + + target._eventsCount. } else {/ * existing is a function description that has registered this event once before. Otherwise, if existing is an array, it will be inserted directly into the corresponding position * / if (typeof existing = 'function') {existing = events [type] = prepend? [listener, existing]: [existing, listener];} else if (prepend) {existing.unshift (listener);} else {existing.push (listener);} / / handle alarms. Too many processing functions may be due to the memory leak m = _ getMaxListeners (target) caused by the previous deletion. / / the event handler reaches the threshold and prompts if (m > 0 & & existing.length > m & &! existing.warned) {existing.warned = true; const w = new Error ('error message …') ; w.name = 'MaxListenersExceededWarning'; w.emitter = target; w.type = type; w.count = existing.length; process.emitWarning (w);}} return target;}
Next, let's take a look at the implementation of once, which is relatively complex compared with other api,once implementations, because we need to control the handler to be executed at most once, so we need to ensure that when the event is triggered, when the user-defined function is executed, we also need to delete the registered event.
VentEmitter.prototype.once = function once (type, listener) {this.on (type, _ onceWrap (this, type, listener)); return this;; unction onceWrapper () {/ / has not triggered if (! this.fired) {/ / delete it this.target.removeListener (this.type, this.wrapFn); / / triggered this.fired = true / / execute if (arguments.length = 0) return this.listener.call (this.target); return this.listener.apply (this.target, arguments);}} / support once api function _ onceWrap (target, type, listener) {/ / fired whether the handler function has been executed. WrapFn wraps the listener function const state = {fired: false, wrapFn: undefined, target, type, listener} / / generate a function that wraps listener const wrapped = onceWrapper.bind (state); / * add the original function listener to the wrapper function to delete it before the event is triggered. See removeListener * / wrapped.listener = listener; / / to save the package function and delete it after execution. See onceWrapper state.wrapFn = wrapped; return wrapped;}.
The Once function constructs a context (state) to store information such as the user's processing function and execution status, and then returns a function with that context (state) wrapped to register with the event system through bind. When the event is triggered, the wrapped is first removed in the wrapped function, and then the user's function is executed. Wrapped played the role of hijacking. In addition, the function passed by the user needs to be saved on the wrapped. When the user deletes the event or releases the function before the event is triggered, during the process of traversing the handler of this kind of event, you can find the corresponding item to delete through wrapped.listener.
3 trigger event after analyzing the subscription of the event, let's take a look at the trigger of the event.
EventEmitter.prototype.emit = function emit (type,... args) {/ / whether the event triggered is an error,error event requires special handling let doError = (type = = 'error'); const events = this._events / defined handlers (not necessarily handlers for type events) if (events! = = undefined) {/ * if the event triggered is error and the kErrorMonitor event is monitored, the kErrorMonitor event is triggered * / if (doError & & events [kErrorMonitor]! = = undefined) this.emit (kErrorMonitor,... args) / / triggers an error event but does not define a handler doError = (doError & & events.error = undefined);} else if (! doError) / / No handler is defined and does not need to be handled if it does not trigger an error event, return false; / / If there is no 'error' event listener then throw. / / if the error event is triggered, but no function is defined to handle the error event, an error if (doError) {let er; if (args.length > 0) er = args [0] is reported; / / the first input parameter is the instance if (er instanceof Error) of Error {try {const capture = {} / * inject the stack attribute into the capture object. The value of stack is the current stack information for executing the Error.captureStackTrace statement, but does not include the part of 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} / / get the handler function const handler = events [type] corresponding to the type event; / / if not, if (handler = = undefined) return false is not processed / / the function indicates that there is only one if (typeof handler = 'function') {/ / directly execute const result = ReflectApply (handler, this, args); / / if it is not empty to determine whether it is promise and whether it needs to be processed, see addCatch if (result! = = undefined & & result! = = null) {addCatch (this, result, type, args) }} else {/ / multiple processing functions, as above 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; iMel -) {this.removeListener (type, listeners [I]);}} return this;}
The main logic of the removeAllListeners function has two points. The first is that the removeListener event requires special handling, which is similar to a hook, which is triggered every time the user deletes the event handler. The second is the removeListener function. RemoveListener is the implementation that actually deletes the event handler. RemoveAllListeners is the logic that encapsulates removeListener.
Function removeListener (type, listener) {let originalListener; const events = this._events; / / nothing to delete if (events = = undefined) return this; const list = events [type]; / / ditto if (list = undefined) return this / / list is a function description that there is only one handler, otherwise it is an array. If list.listener = listener indicates that it is an if registered by once (list = listener | | list.listener = listener) {/ / type type handler is only one, and no other types of events are registered, initialize _ events if (--this._eventsCount = 0) this._events = ObjectCreate (null) Else {/ / after deleting the attribute delete events corresponding to type [type]; / / if the removeListener event is registered, then register the removeListener event if (events.removeListener) this.emit ('removeListener', type, list.listener | | listener) }} else if (typeof list! = = 'function') {/ / multiple handlers let position =-1; / / find the function for that needs to be deleted (let I = list.length-1; I > = 0 Listener -) {if (list [I] = listener | | if [I] .listener = listener) {/ / save the original handler, if any, originalListener = Listl [I] .listener; position = I; break;}} if (listener < 0) return this / / the first one comes out, otherwise delete an if (position = 0) list.shift (); else {if (spliceOne = undefined) spliceOne = require ('internal/util') .spliceOne; spliceOne (list, position) } / / if only one is left, the value will be changed to function type if (list.length = 1) events [type] = list [0]; / / trigger removeListener if (events.removeListener! = = undefined) this.emit ('removeListener', type, originalListener | | listener) } return this;}; at this point, I believe you have a deeper understanding of "Nodejs v14 source code analysis of how to use the Event module". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.