39 # The implementation principle of the events module

Observer mode: There will be two classes, the observer will be stored in the observed, if the state of the observed changes, the observer will be actively notified, and the update method of the observer will be called

Publish and subscribe benefits: can be decoupled

const EventEmitter = require("events");

// 使用自己实现的 events 类
// const EventEmitter = require("./39/events.js");

const util = require("util");
function Man() {
    
    }
// 继承
util.inherits(Man, EventEmitter);
// 创建实例
let man = new Man();

// 发布订阅
const sleep = (name) => console.log(name, "睡");
const eat = (name) => console.log(name, "吃");

newListener has a fixed writing method , which will be triggered every time binding, and can be used to determine which events are being monitored: https://nodejs.org/dist/latest-v18.x/docs/api/events.html#event-newlistener

man.on("newListener", (type) => {
    
    
    console.log("newListener---->", type);
    if (type === "唱跳rap篮球") {
    
    
        // 在当前同步代码执行完毕后触发事件
        process.nextTick(() => {
    
    
            man.emit(type, "小坤");
        });
    }
});

// newListener 测试
man.on("唱跳rap篮球", sleep);
man.once("唱跳rap篮球", eat);

insert image description here

on method :

man.on("唱跳rap篮球", sleep);
man.on("唱跳rap篮球", eat);
man.emit("唱跳rap篮球", "小坤");

insert image description here

off method :

man.on("唱跳rap篮球", sleep);
man.off("唱跳rap篮球", sleep);
man.emit("唱跳rap篮球", "小坤");

insert image description here

once method :

man.once("唱跳rap篮球", eat);
man.emit("唱跳rap篮球", "小坤");
man.emit("唱跳rap篮球", "小坤");

insert image description here

Implement a simple version of EventEmitter yourself

Let's implement the five methods mentioned above:newListener、on、emit、off、once

function EventEmitter() {
    
    
    this._events = {
    
    }; // 默认给 EventEmitter 准备的
}

// 订阅
EventEmitter.prototype.on = function (eventName, callback) {
    
    
    if (!this._events) this._events = {
    
    };
    // 如果不是 newListener 那就需要触发 newListener 的回调
    if (eventName !== "newListener") {
    
    
        this.emit("newListener", eventName);
    }
    if (!this._events[eventName]) this._events[eventName] = [];
    this._events[eventName].push(callback);
};
// 发布
EventEmitter.prototype.emit = function (eventName, ...args) {
    
    
    if (this._events && this._events[eventName]) {
    
    
        this._events[eventName].forEach((event) => event(...args));
    }
};
// 注销
EventEmitter.prototype.off = function (eventName, callback) {
    
    
    if (this._events && this._events[eventName]) {
    
    
        // 这里需要对 once 里的进行处理:删除时获取 once 里的 l 属性和 callback 比较,如果相等也需要删除
        this._events[eventName] = this._events[eventName].filter((cb) => cb != callback && cb.l != callback);
    }
};
// 订阅只执行一次
EventEmitter.prototype.once = function (eventName, callback) {
    
    
    // 使用切片
    const once = (...args) => {
    
    
        callback(...args);
        this.off(eventName, once);
    };
    // 给 once 添加 l 属性用于表示 callback
    once.l = callback;
    // 先绑定一个一次性事件,稍后触发时在将事件清空
    this.on(eventName, once);
};

module.exports = EventEmitter;

events source code overview

Configure firstlanch.json

{
    
    
    // 使用 IntelliSense 了解相关属性。 
    // 悬停以查看现有属性的描述。
    // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
    
    
            "type": "node",
            "request": "launch",
            "name": "启动程序",
            // 这里我们不跳过 node 的内部源码,因为等下需要调试 require,需注释掉
            "skipFiles": [
                // "<node_internals>/**"
            ],
            "program": "${workspaceFolder}\\doc\\39 # events 模块的实现原理.js"
        }
    ]
}

Then set a breakpoint at the calling method and enter single-step debugging

insert image description here

on method: alias, addListenerthe underlying call is_addListener

insert image description here

The source code is written here Object.create(null), it {}is different from , it does not have a prototype chain, but {}has a prototype chain.

insert image description here
newListener method: We can see that there is a process for _addListenerin newListenerwhich will trigger newListener first.

insert image description here

emit method:

insert image description here

Here we need to mention a primordials: (the primordials variable is an appearance of many types of native js constructors and methods inside nodejs, to prevent errors when the native js constructors and methods are overwritten) The apply here comes from primordialsReflect.apply

insert image description here

off method: aliasremoveListener

insert image description here
It is also a similar judgment method:list[i] === listener || list[i].listener === listener
insert image description here

once method: the bottom layer is called _onceWrap, and the listener is hung wrapped.listeneron , what we implement ourselves is to hang ononce.l

insert image description here

Guess you like

Origin blog.csdn.net/kaimo313/article/details/131298407