Events

描述

  • 大多数 Node.js API 采用异步事件驱动架构,这些对象都是EventEmitter类的实例(Emitter),通过触发命名事件(eventName or type)来调用函数(监听器,listener)
  • Emitter 触发事件时,可以向监听器函数传递任意数量的参数,所有注册到该事件上的监听器函数都会依次同步执行,函数的返回值会被忽略

事件

  • 命名规范:驼峰式字符串级任何有效的 JavaScript 属性键

error

error 事件被视为特殊情况,如果没有注册监听器会导致抛出错误、打印堆栈跟踪并退出 Node.js 进程,应始终为error事件注册监听器

errorMonitor

  • errorMonitor 事件注册的监听器不会消耗 error,如果没有为 error 事件注册监听器,依然会导致抛出错误、打印堆栈跟踪并退出 Node.js 进程。

    'use strict'
    const EventEmitter = require('events').EventEmitter;
    const ee = new EventEmitter({ captureRejections: true });
    ee.on(EventEmitter.errorMonitor, function () {
    console.log('ErrorMonitor, call first');
    })
    ee.on('error', () => {
    console.log('customer error listener');
    })
    ee.emit('error');

newListener

  • 当有新的监听器被添加时,所有 Emitter 都会触发 'newListener' 事件。

  • 为该事件注册监听器相等于一个钩子函数,可以获取到事件的名称要添加的监听器的引用

    class MyEmitter extends EventEmitter {}
    
    const myEmitter = new MyEmitter();
    // 只处理一次,避免无限循环。
    myEmitter.once('newListener', (event, listener) => {
    console.log(`为${event}添加事件${listener}`)
    })

removeListener

  • 当现有的监听器被移除时,所有 Emitter 都会触发 'removeListener' 事件。
  • 为该事件注册监听器相等于一个钩子函数,可以获取到事件的名称要添加的监听器的引用

监听器

this指向

普通函数中的this是触发事件的Emitter,箭头函数中的this是{}

const EventEmitter = require('events').EventEmitter;
const ee = new EventEmitter();
function f1() {
console.log('run f1, this = ', this);
}
const f2 = () => {
console.log('run arrow function, this = ', this);
}
ee.on('test', f1);
ee.on('test', f2);
ee.emit('test');

异步监听器

如果添加异步监听器,需要开启captureRejections 选项且实现captureRejectionSymbol方法

const { EventEmitter, captureRejectionSymbol } = require('events');
const ee = new EventEmitter({ captureRejections: true });
// 3中方式获取 kRejection 的 Symbol 值
ee[ee.constructor.captureRejectionSymbol] = function (err, event, ...args) {
console.log('rejection happened for', event, 'with', err, ...args);
}
ee[EventEmitter.captureRejectionSymbol] = function (err, event, ...args) {
console.log('rejection happened for', event, 'with', err, ...args);
}
ee[captureRejectionSymbol] = function (err, event, ...args) {
console.log('rejection happened for', event, 'with', err, ...args);
}
async function f() {
return Promise.reject('async rejection');
}
ee.on('test', f);
ee.emit('test');

文档中这句话没太明白

The 'error' events that are generated by the captureRejections behavior do not have a catch handler to avoid infinite error loops: the recommendation is to not use async functions as 'error' event handlers.

执行次数

  • 通过EventEmitter#on() 方式注册的监听器,每次触发命名事件都会执行

  • 通过EventEmitter#once() 方式注册的监听器,触发命名事件只会执行一次

    触发once事件时,先触发removeListener事件移除监听器,再调用执行。在removeListener监听器中可拿到 once 监听器,可多次执行

    const EventEmitter = require('events').EventEmitter;
    
    let ee = new EventEmitter()
    
    ee.once('test', () => console.log('test'))
    
    ee.on('removeListener', (eventName, listener) => {
    console.log(`eventName: ${eventName}`)
    listener() // 多次执行
    listener()
    listener()
    }) ee.emit('test')
    // eventName: test
    // test
    // test
    // test
    // test

源码 v16.10

代码注释 https://github.com/lfp1024/node/blob/master/lib/events.js

常量

const kRejection = SymbolFor('nodejs.rejection');
const kCapture = Symbol('kCapture');
const kErrorMonitor = Symbol('events.errorMonitor');
const kMaxEventTargetListeners = Symbol('events.maxEventTargetListeners');
const kMaxEventTargetListenersWarned =
Symbol('events.maxEventTargetListenersWarned');

前缀 k 表示常量 (德语 konstant)

构造函数

function EventEmitter(opts) {
EventEmitter.init.call(this, opts); // 使 init 方法中的 this 指向新创建的实例,而非 EventEmitter 本身
}
module.exports = EventEmitter;
  • opts

    captureRejections

    • 类型:Boolean
    • 描述:是否开启自动捕获异步监听器的 rejection
      • false「默认」不开启
      • true 开启

静态属性

captureRejectionSymbol

用来自定义异步监听器 rejection 的处理方法

EventEmitter.captureRejectionSymbol = kRejection;

errorMonitor

事件名称

在该事件上注册的监听器,只监听error事件,且在常规error事件监听器调用之前被调用,不消耗 error

EventEmitter.errorMonitor = kErrorMonitor;

captureRejections

是否自动捕获异步监听器的rejection

ObjectDefineProperty(EventEmitter, 'captureRejections', {
get() {
return EventEmitter.prototype[kCapture]; // 返回原型上的 kCapture
},
set(value) {
if (typeof value !== 'boolean') {
throw new ERR_INVALID_ARG_TYPE('EventEmitter.captureRejections',
'boolean', value);
}
EventEmitter.prototype[kCapture] = value; // 设置原型上的 kCapture
},
enumerable: true
});

defaultMaxListeners

单个事件默认最大可注册监听器个数

  • 默认情况下,每个事件可以最多注册 10 个监听器
  • 可以使用 EventEmitter.defaultMaxListeners 属性改变所有 EventEmitter 实例的默认值(包括之前创建的)。 如果此值不是一个正数,则抛出 RangeError
let defaultMaxListeners = 10;
ObjectDefineProperty(EventEmitter, 'defaultMaxListeners', {
enumerable: true,
get: function() {
return defaultMaxListeners;
},
set: function(arg) {
if (typeof arg !== 'number' || arg < 0 || NumberIsNaN(arg)) {
throw new ERR_OUT_OF_RANGE('defaultMaxListeners',
'a non-negative number',
arg);
}
defaultMaxListeners = arg;
}
});

静态方法

init

实例初始化

EventEmitter.init = function(opts) {
// new EventEmitter(),在init方法执行之前,先创建了一个对象,this就指向该对象
if (this._events === undefined ||
this._events === ObjectGetPrototypeOf(this)._events) { // 避免给原型添加`_events`属性,导致所有实例共享
this._events = ObjectCreate(null); // 纯粹的键值对存储对象,没有原型
this._eventsCount = 0;
}
// 如果原型上有_maxListener则新实例继承(_events不可以继承,_maxListener可以继承)
this._maxListeners = this._maxListeners || undefined; if (opts?.captureRejections) {
if (typeof opts.captureRejections !== 'boolean') {
throw new ERR_INVALID_ARG_TYPE('options.captureRejections',
'boolean', opts.captureRejections);
}
this[kCapture] = Boolean(opts.captureRejections);
} else {
// Assigning the kCapture property directly saves an expensive
// prototype lookup in a very sensitive hot path.
this[kCapture] = EventEmitter.prototype[kCapture]; // 默认值「false」
}
};

setMaxListeners

EventEmitter.setMaxListeners = function(n = defaultMaxListeners, ...eventTargets) {
if (typeof n !== 'number' || n < 0 || NumberIsNaN(n))
throw new ERR_OUT_OF_RANGE('n', 'a non-negative number', n);
if (eventTargets.length === 0) {
defaultMaxListeners = n;
} else {
if (isEventTarget === undefined)
isEventTarget = require('internal/event_target').isEventTarget; for (let i = 0; i < eventTargets.length; i++) {
const target = eventTargets[i];
if (isEventTarget(target)) {
target[kMaxEventTargetListeners] = n;
target[kMaxEventTargetListenersWarned] = false;
} else if (typeof target.setMaxListeners === 'function') {
target.setMaxListeners(n);
} else {
throw new ERR_INVALID_ARG_TYPE(
'eventTargets',
['EventEmitter', 'EventTarget'],
target);
}
}
}
};

示例

const { EventEmitter } = require('events');

const ee = new EventEmitter();
console.log(ee.getMaxListeners()); EventEmitter.setMaxListeners(5, ee) // 可以用其静态方法修改某个ee的最大监听器个数
console.log(ee.getMaxListeners());

原型属性

Symbol('kCapture')

在原型和实例上各有一份,节省到原型上查找的开销

是否捕获异步监听器的rejection

const kCapture = Symbol('kCapture');
ObjectDefineProperty(EventEmitter.prototype, kCapture, {
value: false,
writable: true,
enumerable: false
}); // 获取方式
const ee = new EventEmitter();
console.log('ee.kCapture = ', ee[Reflect.ownKeys(ee)[3]]); // false

实例属性

Symbol('kCapture')

在原型和实例上各有一份,节省到原型上查找的开销

是否捕获异步监听器的rejection

// init 方法中
// Assigning the kCapture property directly saves an expensive
// prototype lookup in a very sensitive hot path.
this[kCapture] = EventEmitter.prototype[kCapture]; // 默认值「false」

_events

保存 监听事件 和 注册在该事件上的监听器

EventEmitter.prototype._events = undefined;

_eventsCount

当前实例中监听事件的个数

EventEmitter.prototype._eventsCount = 0;

_maxListeners

当前实例单个事件最大可注册监听器个数

EventEmitter.prototype._maxListeners = undefined;

实例方法

添加

on

同 addListener

EventEmitter.prototype.on = EventEmitter.prototype.addListener;
addListener

把监听器添加到指定事件监听器数组的末尾,多次添加相同的监听器会多次调用

返回实例的引用,以便可以链式调用

EventEmitter.prototype.addListener = function addListener(type, listener) {
return _addListener(this, type, listener, false); // 原型上的方法提出去,将this传入即可
}; function _addListener(target, type, listener, prepend) {
let m;
let events;
let existing; checkListener(listener); // 检测listener是否为function,否则抛异常 events = target._events;
if (events === undefined) { // 未初始化
events = target._events = ObjectCreate(null);
target._eventsCount = 0;
} else {
// To avoid recursion in the case that type === "newListener"! Before
// adding it to the listeners, first emit "newListener".
if (events.newListener !== undefined) { // 是否监听 `newListener` 事件,每次注册监听器都会触发,类似钩子
target.emit('newListener', type,
listener.listener ? listener.listener : listener); // Re-assign `events` because a newListener handler could have caused the
// this._events to be assigned to a new object
events = target._events; // 重新赋值
}
existing = events[type];
} if (existing === undefined) { // 之前没有监听该事件
// Optimize the case of one listener. Don't need the extra array object.
events[type] = listener; //events 和 target._events 指向同一个对象
++target._eventsCount;
} else { // 之前已经监听该事件
if (typeof existing === 'function') {
// Adding the second element, need to change to array.
existing = events[type] =
prepend ? [listener, existing] : [existing, listener];
// If we've already got an array, just append.
} else if (prepend) {
existing.unshift(listener);
} else {
existing.push(listener);
} // Check for listener leak 检测最大监听器个数
m = _getMaxListeners(target);
if (m > 0 && existing.length > m && !existing.warned) {
existing.warned = true; // 是否多余?
// No error code for this since it is a Warning
// eslint-disable-next-line no-restricted-syntax
const w = new Error('Possible EventEmitter memory leak detected. ' +
`${existing.length} ${String(type)} listeners ` +
`added to ${inspect(target, { depth: -1 })}. Use ` +
'emitter.setMaxListeners() to increase limit');
w.name = 'MaxListenersExceededWarning';
w.emitter = target;
w.type = type;
w.count = existing.length;
process.emitWarning(w); // 发出警告
}
}
return target;
}
prependListener

把监听器添加到指定事件的监听器数组开头

返回实例的引用,以便可以链式调用

EventEmitter.prototype.prependListener =  function prependListener(type, listener) {
return _addListener(this, type, listener, true);
};
once

添加单次监听器到指定事件的监听器数组末尾,触发的时候先移除再执行

返回实例的引用,以便可以链式调用

function 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);
}
} function _onceWrap(target, type, listener) {
const state = { fired: false, wrapFn: undefined, target, type, listener };
const wrapped = onceWrapper.bind(state);
wrapped.listener = listener; // 挂载原始监听器,一同传递给 removeListener 方法
state.wrapFn = wrapped; // 挂载包裹后的监听器,用于移除
return wrapped;
} EventEmitter.prototype.once = function once(type, listener) {
checkListener(listener); this.on(type, _onceWrap(this, type, listener)); // 通过 once 监听的事件获取监听器的方式为 listener.listener
return this;
};
prependOnceListener

添加单次监听器到指定事件的监听器数组开头

返回实例的引用,以便可以链式调用

EventEmitter.prototype.prependOnceListener = function prependOnceListener(type, listener) {
checkListener(listener); this.prependListener(type, _onceWrap(this, type, listener));
return this;
};

移除

off

同 removeListener

EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
removeListener

从指定事件的监听器数组中移除指定的监听器

在事件触发之后,最后一个监听器执行完成之前, 移除监听器不会影响已触发的监听器执行

返回实例的引用,以便可以链式调用

EventEmitter.prototype.removeListener = function removeListener(type, listener) {
checkListener(listener); const events = this._events;
if (events === undefined) // 未初始化
return this; const list = events[type];
if (list === undefined)
return this;
// 事件只有一个 listener,则会发生 list === listener( once 注册的listener 为 list.listener)
if (list === listener || list.listener === listener) {
if (--this._eventsCount === 0) // event实例只有一个监听事件
this._events = ObjectCreate(null);
else { // event实例有多个监听事件
delete events[type]; // 先移除后触发
if (events.removeListener) // 如果监听了 `removeListener` 事件,则触发,类似钩子
this.emit('removeListener', type, list.listener || listener);
}
} else if (typeof list !== 'function') { // events[type] 只可能是函数或数组类型,这里判断非函数,则为数组类型?
let position = -1; // 利用 `-1` 这个标志 for (let i = list.length - 1; i >= 0; i--) {
if (list[i] === listener || list[i].listener === listener) {
position = i;
break;
}
} if (position < 0)
return this; if (position === 0)
list.shift();
else {
if (spliceOne === undefined)
spliceOne = require('internal/util').spliceOne; // 类似 Array#splice(),但是速度较快
spliceOne(list, position);
} if (list.length === 1)
events[type] = list[0]; // 单个listener不用数组 if (events.removeListener !== undefined)
this.emit('removeListener', type, listener);
} return this;
};
removeAllListeners

移除所有监听器或指定事件的所有监听器

返回实例的引用,以便可以链式调用

EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) {
const events = this._events;
if (events === undefined) // 未初始化
return this; // 逻辑1: 没有注册 removeListener 事件
// Not listening for removeListener, no need to emit
if (events.removeListener === undefined) {
if (arguments.length === 0) { // 等价于没传参数,type === undefined,删除所有监听事件
this._events = ObjectCreate(null);
this._eventsCount = 0;
} else if (events[type] !== undefined) {
if (--this._eventsCount === 0) // event实例只有一个监听事件
this._events = ObjectCreate(null);
else
delete events[type];
}
return this;
}
// 逻辑2: 注册了 removeListener 事件
// Emit removeListener for all listeners on all events
if (arguments.length === 0) { // 删除所有监听事件
for (const key of ReflectOwnKeys(events)) { // 较 Object#keys(),Reflect#ownKeys() 包含 Symbol 值的属性名
if (key === 'removeListener') continue; // 把其他事件删除后,再删除。如果先删除,后面的事件就走逻辑1了
this.removeAllListeners(key); // 递归,走逻辑3,然后返回
}
this.removeAllListeners('removeListener'); // 递归,走逻辑3,然后返回
this._events = ObjectCreate(null);
this._eventsCount = 0;
return this;
}
// 逻辑3: 真正删除事件监听器
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;
};

修改

setMaxListeners

修改当前 EventEmitter 实例单个事件最大监听器个数。设为 Infinity(或 0)表示不限制监听器的数量

返回实例的引用,以便可以链式调用

EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) {
if (typeof n !== 'number' || n < 0 || NumberIsNaN(n)) {
throw new ERR_OUT_OF_RANGE('n', 'a non-negative number', n);
}
this._maxListeners = n;
return this;
};
getMaxListeners

返回当前EventEmitter 实例单个事件最大监听器个数

EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
return _getMaxListeners(this); // 把 this(实例)传进去
}; function _getMaxListeners(that) {
if (that._maxListeners === undefined)
return EventEmitter.defaultMaxListeners;
return that._maxListeners;
}

获取

listeners

返回一个数组,包含指定事件的所有监听器(拆包之后)

function _listeners(target, type, unwrap) {
const events = target._events; if (events === undefined)
return []; const evlistener = events[type];
if (evlistener === undefined)
return []; if (typeof evlistener === 'function')
return unwrap ? [evlistener.listener || evlistener] : [evlistener]; return unwrap ?
unwrapListeners(evlistener) : arrayClone(evlistener);
} EventEmitter.prototype.listeners = function listeners(type) {
return _listeners(this, type, true);
};
rawListeners

返回一个数组,包含指定事件的所有(原始)监听器

如果是通过 once 注册的监听器,则返回的是被包装(wrap)后的监听器

EventEmitter.prototype.rawListeners = function rawListeners(type) {
return _listeners(this, type, false);
};
listenerCount

返回指定事件上注册的监听器个数

EventEmitter.prototype.listenerCount = listenerCount;
function listenerCount(type) {
const events = this._events; if (events !== undefined) {
const evlistener = events[type]; if (typeof evlistener === 'function') {
return 1;
} else if (evlistener !== undefined) {
return evlistener.length;
}
} return 0;
}
eventNames

返回一个数组,包含所有监听事件。元素类型为 String 或 Symbol

EventEmitter.prototype.eventNames = function eventNames() { // 所有监听事件
return this._eventsCount > 0 ? ReflectOwnKeys(this._events) : [];
};

触发

emit

触发指定事件并同步调用注册到该事件上的所有监听器

如果事件有监听器,则返回 true,否则返回 false

EventEmitter.prototype.emit = function emit(type, ...args) {
let doError = (type === 'error'); // error 事件,特殊处理 const events = this._events;
if (events !== undefined) { // 有监听事件
if (doError && events[kErrorMonitor] !== undefined) // 如果是 error 事件,且用户添加了 kErrorMonitor 监听器
this.emit(kErrorMonitor, ...args);
doError = (doError && events.error === undefined); // 如果是 error 事件,且没有对应的 handler
} else if (!doError) // events === undefined && !doError => 未初始化且不是 error 事件,则返回 false
return false; // 触发 error 事件且没有handler,抛出异常(参数中的error实例或自己生成的error)
// If there is no 'error' event listener then throw.
if (doError) {
// 1. 有监听事件 && 触发error事件 && 没有error事件的 handler
// 2. 未初始化(肯定也没有error事件的 handler) && 触发error事件
let er;
if (args.length > 0)
er = args[0];
if (er instanceof Error) { // 第一个入参是 Error 实例
try { // 给 er 添加当前栈信息(emit部分)
const capture = {};
ErrorCaptureStackTrace(capture, EventEmitter.prototype.emit);
ObjectDefineProperty(er, kEnhanceStackBeforeInspector, {
value: enhanceStackTrace.bind(this, er, capture),
configurable: true
});
} catch {} // 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
} let stringifiedEr;
const { inspect } = require('internal/util/inspect');
try {
stringifiedEr = inspect(er);
} catch {
stringifiedEr = er;
} // At least give some kind of context to the user
const err = new ERR_UNHANDLED_ERROR(stringifiedEr);
err.context = er;
throw err; // Unhandled 'error' event
} const handler = events[type]; // 如果是 error 事件,至此一定有handler,如果是其他类型事件,仍然需要判断handler的存在性
if (handler === undefined)
return false;
// 单个handler是函数,多个handler是数组
if (typeof handler === 'function') {
const result = handler.apply(this, args); // 参见 https://github.com/nodejs/node/pull/38248 // We check if result is undefined first because that
// is the most common case so we do not pay any perf
// penalty
if (result !== undefined && result !== null) {
addCatch(this, result, type, args); // 如果是handler是异步函数,捕获其rejection异常
}
} else {
const len = handler.length; // 确定监听器的个数,避免监听器中再监听同一个事件造成死循环
const listeners = arrayClone(handler); // 克隆handler数组,防止在事件触发后,已注册监听器的变动
for (let i = 0; i < len; ++i) {
const result = listeners[i].apply(this, args); // 同步依次调用 // We check if result is undefined first because that
// is the most common case so we do not pay any perf
// penalty.
// This code is duplicated because extracting it away
// would make it non-inlineable.
if (result !== undefined && result !== null) {
addCatch(this, result, type, args);
}
}
} return true;
}; function addCatch(that, promise, type, args) {
if (!that[kCapture]) {
return;
}
// 符合 Promises/A+ 规范的 promise 的属性 then 可能具有 get 方法,二次调用可能会报错,因此采用call方式
// 因此下面采用的是 call 方式调用
// Handle Promises/A+ spec, then could be a getter
// that throws on second use.
try {
const then = promise.then; if (typeof then === 'function') {
then.call(promise, undefined, function(err) {
// The callback is called with nextTick to avoid a follow-up // follow-up rejection ?
// rejection from this promise.
process.nextTick(emitUnhandledRejectionOrErr, that, err, type, args);
});
}
} catch (err) {
that.emit('error', err);
}
} function emitUnhandledRejectionOrErr(ee, err, type, args) {
if (typeof ee[kRejection] === 'function') { // 实例(用户)自己实现了 kRejection 函数
ee[kRejection](err, type, ...args);
} else {
// We have to disable the capture rejections mechanism, otherwise
// we might end up in an infinite loop. // 防止 error 事件 handler 也抛出 error,导致死循环。
// 先保存之前的值,再关闭,如果程序未「退出」再恢复之前的值
const prev = ee[kCapture]; // If the error handler throws, it is not catcheable and it
// will end up in 'uncaughtException'. We restore the previous
// value of kCapture in case the uncaughtException is present
// and the exception is handled. // 如果没有自定义 rejected promise 的处理函数,则走 error 事件的处理函数
// 如果 error 事件 handler 也抛出 error,则不会被捕获,nodejs 会触发 uncaughtException 事件并「退出」
// 如果用户处理了 uncaughtException 事件(通过 process#on() 处理),则恢复 kRejection 的值
try {
ee[kCapture] = false;
ee.emit('error', err);
} finally {
ee[kCapture] = prev;
}
}
}

其他

primordials

primordials对象用来保证内建模块可以访问真正的不受用户干扰的全局变量

https://stackoverflow.com/a/60215269/11089100

spliceOne

更高效的移除数组中的元素

function spliceOne(list, index) {
for (; index + 1 < list.length; index++)
list[index] = list[index + 1]; // 从被移除元素开始,将后面的元素值依次向前复制
list.pop(); // 移除最后一个元素
}

创建实例未初始化

'use strict';

const { EventEmitter, captureRejectionSymbol } = require('events');

function MyEventEmitter() {
// EventEmitter.call(this);
} MyEventEmitter.prototype = EventEmitter.prototype;
MyEventEmitter.prototype.constructor = MyEventEmitter const ee = new MyEventEmitter(); // 上述创建 EventEmitter 实例的方式并没有调用 EventEmitter.init() 初始化,因此实例属性值都是 undefined
console.log('ee._events = ', ee._events);
console.log('ee._eventsCount = ', ee._eventsCount);
ee.on('test', () => { console.log('tttttttt') });
ee.emit('test');
console.log('ee.eventNames = ',ee.eventNames());

参考

通过源码解析 Node.js 中 events 模块里的优化小细节

通过源码分析nodejs原理

中文文档

Node.js-Events 模块总结与源码解析的更多相关文章

  1. vue系列---Mustache.js模板引擎介绍及源码解析(十)

    mustache.js(3.0.0版本) 是一个javascript前端模板引擎.官方文档(https://github.com/janl/mustache.js) 根据官方介绍:Mustache可以 ...

  2. Android FM 模块学习之四 源码解析(1)

    Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE MicrosoftInternetExplorer4 前一章我们了解了FM手动调频,接下 ...

  3. Android FM模块学习之四源码解析(一)

    转自:http://blog.csdn.net/tfslovexizi/article/details/41516149?utm_source=tuicool&utm_medium=refer ...

  4. Celery 源码解析六:Events 的实现

    在 Celery 中,除了远程控制之外,还有一个元素可以让我们对分布式中的任务的状态有所掌控,而且从实际意义上来说,这个元素对 Celery 更为重要,这就是在本文中将要说到的 Event. 在 Ce ...

  5. [源码解析] 并行分布式框架 Celery 之 worker 启动 (1)

    [源码解析] 并行分布式框架 Celery 之 worker 启动 (1) 目录 [源码解析] 并行分布式框架 Celery 之 worker 启动 (1) 0x00 摘要 0x01 Celery的架 ...

  6. [源码解析] 并行分布式框架 Celery 之 worker 启动 (2)

    [源码解析] 并行分布式框架 Celery 之 worker 启动 (2) 目录 [源码解析] 并行分布式框架 Celery 之 worker 启动 (2) 0x00 摘要 0x01 前文回顾 0x2 ...

  7. Celery 源码解析五: 远程控制管理

    今天要聊的话题可能被大家关注得不过,但是对于 Celery 来说确实很有用的功能,曾经我在工作中遇到这类情况,就是我们将所有的任务都放在同一个队列里面,然后有一天突然某个同学的代码写得不对,导致大量的 ...

  8. Celery 源码解析三: Task 对象的实现

    Task 的实现在 Celery 中你会发现有两处,一处位于 celery/app/task.py,这是第一个:第二个位于 celery/task/base.py 中,这是第二个.他们之间是有关系的, ...

  9. [源码解析] 分布式任务队列 Celery 之启动 Consumer

    [源码解析] 分布式任务队列 Celery 之启动 Consumer 目录 [源码解析] 分布式任务队列 Celery 之启动 Consumer 0x00 摘要 0x01 综述 1.1 kombu.c ...

随机推荐

  1. [Design Pattern With Go]设计模式-单例模式

    定义 一个类只允许创建一个对象(或者实例),那这个类就是一个单例类,这种设计模式就叫作单例模式.当某些数据只需要在系统中保留一份的时候,可以选择使用单例模式. 饿汉式 饿汉式的实现方式比较简单.在类加 ...

  2. 【Azure Developer】Github Action部署资源(ARM模板)到Azure中国区时,遇见登录问题的解决办法

    问题描述 在参考文档"使用 GitHub Actions 部署 ARM 模板"一文中,由于是在中国区Azure上操作,所以生产的部署凭证为中国区凭证.当创建工作流时,在登录到Azu ...

  3. bootstrap期末考试习题整理

    1.Which is true about Bootstrap? A. Bootstrap is the most popular and powerful front-end (HTML, CSS, ...

  4. Python的flask接收前台的ajax的post数据和get数据

    ajax向后台发送数据: ①post方式 ajax: @app.route("/find_worldByName",methods=['POST']) type:'post', d ...

  5. Dynamics CRM9.0更新了Chrome后菜单按钮变形

    前段时间Chorme更新后Dynamics CRM9.0的系统菜单样式变的很难看 具体修改方法如下: 找到Dynamics CRM安装目录C:\Program Files\Microsoft Dyna ...

  6. Linux apt命令使用 以及 文本流和重定向

    apt (Advanced Packaging Tool) 是一个在Debian和Ubuntu中的Shell前端软件包管理器. apt命令执行需要超级管理员权限(root). apt语法 apt [o ...

  7. Day05_20_Java中类的创建和对象的实例化

    类的创建和对象的实例化 类的定义 类属于引用数据类型,java语言中所有的.class都属于引用数据类型 在类体当中,方法体之外定义的变量被称为 成员变量,成员变量没有赋值,系统默认是0: 语法结构: ...

  8. 1035 Password

    To prepare for PAT, the judge sometimes has to generate random passwords for the users. The problem ...

  9. IDEA中集成Git

    一.新建项目,绑定GIT 1.新建spring boot项目 2.路径选择git本地文件地址 3.新的项目文件绑定git,将远程的git文件拷贝至项目中  二.修改文件,使用IDEA操作GIT 1.提 ...

  10. 17- web测试面试题