zoukankan      html  css  js  c++  java
  • Node中的EventEmitter? 如何实现一个EventEmitter?

    一、是什么

    我们了解到,Node采用了事件驱动机制,而EventEmitter就是Node实现事件驱动的基础

    EventEmitter的基础上,Node几乎所有的模块都继承了这个类,这些模块拥有了自己的事件,可以绑定/触发监听器,实现了异步操作

    Node.js 里面的许多对象都会分发事件,比如 fs.readStream 对象会在文件被打开的时候触发一个事件

    这些产生事件的对象都是 events.EventEmitter 的实例,这些对象有一个 eventEmitter.on() 函数,用于将一个或多个函数绑定到命名事件上

    二、使用方法

    Nodeevents模块只提供了一个EventEmitter类,这个类实现了Node异步事件驱动架构的基本模式——观察者模式

    在这种模式中,被观察者(主体)维护着一组其他对象派来(注册)的观察者,有新的对象对主体感兴趣就注册观察者,不感兴趣就取消订阅,主体有更新的话就依次通知观察者们

    基本代码如下所示:

    const EventEmitter = require('events')

    class MyEmitter extends EventEmitter {}
    const myEmitter = new MyEmitter()

    function callback() {
        console.log('触发了event事件!')
    }
    myEmitter.on('event', callback)
    myEmitter.emit('event')
    myEmitter.removeListener('event', callback);

    通过实例对象的on方法注册一个名为event的事件,通过emit方法触发该事件,而removeListener用于取消事件的监听

    关于其常见的方法如下:

    • emitter.addListener/on(eventName, listener) :添加类型为 eventName 的监听事件到事件数组尾部
    • emitter.prependListener(eventName, listener):添加类型为 eventName 的监听事件到事件数组头部
    • emitter.emit(eventName[, ...args]):触发类型为 eventName 的监听事件
    • emitter.removeListener/off(eventName, listener):移除类型为 eventName 的监听事件
    • emitter.once(eventName, listener):添加类型为 eventName 的监听事件,以后只能执行一次并删除
    • emitter.removeAllListeners([eventName]):移除全部类型为 eventName 的监听事件

    三、实现过程

    通过上面的方法了解,EventEmitter是一个构造函数,内部存在一个包含所有事件的对象

    class EventEmitter {
        constructor() {
            this.events = {};
        }
    }

    其中events存放的监听事件的函数的结构如下:

    {
      "event1": [f1,f2,f3],
      "event2": [f4,f5],
      ...
    }

    然后开始一步步实现实例方法,首先是emit,第一个参数为事件的类型,第二个参数开始为触发事件函数的参数,实现如下:

    emit(type, ...args) {
        this.events[type].forEach((item) => {
            Reflect.apply(item, this, args);
        });
    }

    当实现了emit方法之后,然后实现onaddListenerprependListener这三个实例方法,都是添加事件监听触发函数,实现也是大同小异

    on(type, handler) {
        if (!this.events[type]) {
            this.events[type] = [];
        }
        this.events[type].push(handler);
    }

    addListener(type,handler){
        this.on(type,handler)
    }

    prependListener(type, handler) {
        if (!this.events[type]) {
            this.events[type] = [];
        }
        this.events[type].unshift(handler);
    }

    紧接着就是实现事件监听的方法removeListener/on

    removeListener(type, handler) {
        if (!this.events[type]) {
            return;
        }
        this.events[type] = this.events[type].filter(item => item !== handler);
    }

    off(type,handler){
        this.removeListener(type,handler)
    }

    最后再来实现once方法, 再传入事件监听处理函数的时候进行封装,利用闭包的特性维护当前状态,通过fired属性值判断事件函数是否执行过

    once(type, handler) {
        this.on(type, this._onceWrap(type, handler, this));
      }

      _onceWrap(type, handler, target) {
        const state = { fired: false, handler, type , target};
        const wrapFn = this._onceWrapper.bind(state);
        state.wrapFn = wrapFn;
        return wrapFn;
      }

      _onceWrapper(...args) {
        if (!this.fired) {
          this.fired = true;
          Reflect.apply(this.handler, this.target, args);
          this.target.off(this.type, this.wrapFn);
        }
     }

    完整代码如下:

    class EventEmitter {
        constructor() {
            this.events = {};
        }

        on(type, handler) {
            if (!this.events[type]) {
                this.events[type] = [];
            }
            this.events[type].push(handler);
        }

        addListener(type,handler){
            this.on(type,handler)
        }

        prependListener(type, handler) {
            if (!this.events[type]) {
                this.events[type] = [];
            }
            this.events[type].unshift(handler);
        }

        removeListener(type, handler) {
            if (!this.events[type]) {
                return;
            }
            this.events[type] = this.events[type].filter(item => item !== handler);
        }

        off(type,handler){
            this.removeListener(type,handler)
        }

        emit(type, ...args) {
            this.events[type].forEach((item) => {
                Reflect.apply(item, this, args);
            });
        }

        once(type, handler) {
            this.on(type, this._onceWrap(type, handler, this));
        }

        _onceWrap(type, handler, target) {
            const state = { fired: false, handler, type , target};
            const wrapFn = this._onceWrapper.bind(state);
            state.wrapFn = wrapFn;
            return wrapFn;
        }

        _onceWrapper(...args) {
            if (!this.fired) {
                this.fired = true;
                Reflect.apply(this.handler, this.target, args);
                this.target.off(this.type, this.wrapFn);
            }
        }
    }

    测试代码如下:

    const ee = new EventEmitter();

    // 注册所有事件
    ee.once('wakeUp', (name) => { console.log(`${name} 1`); });
    ee.on('eat', (name) => { console.log(`${name} 2`) });
    ee.on('eat', (name) => { console.log(`${name} 3`) });
    const meetingFn = (name) => { console.log(`${name} 4`) };
    ee.on('work', meetingFn);
    ee.on('work', (name) => { console.log(`${name} 5`) });

    ee.emit('wakeUp', 'xx');
    ee.emit('wakeUp', 'xx');         // 第二次没有触发
    ee.emit('eat', 'xx');
    ee.emit('work', 'xx');
    ee.off('work', meetingFn);        // 移除事件
    ee.emit('work', 'xx');           // 再次工作

    参考文献

    • http://nodejs.cn/api/events.html#events_class_eventemitter

    本文来自博客园,作者:喆星高照,转载请注明原文链接:https://www.cnblogs.com/houxianzhou/p/14886017.html

  • 相关阅读:
    【QA3】apache2启动失败问题
    查看OS位数
    软件测试开发 SDET
    【QA1】mysql下max_connections问题
    Js 四舍五入
    word中如何设置不连续页码 (转)
    隐藏select边框及下接箭头方法
    where 与 having 的区别
    asp.net 浏览器标题栏加入图标 方法
    AjaxControlToolkit CollapsiblePanelExtender用法
  • 原文地址:https://www.cnblogs.com/houxianzhou/p/14886017.html
Copyright © 2011-2022 走看看