zoukankan      html  css  js  c++  java
  • node.js从入门到放弃(二)

    上章讲了学习node,应该去学习什么,对这些框架去进行学习现在咋们聊聊如何用原生来进行操作

    主要来讲一下events-事件触发器

    events是什么东西呢?他的英文翻译,活动,事件的意思,在计算机语言离也是事件的意思,什么是事件呢?最简单的,鼠标移上是事件吧,点击是吧,些都是事件的一种,这些都是常用的一种事件,现在来讲讲node里的事件

     node.js中事件的发射器 ,也就是调取的东西在这

    class EventEmitter {
      // 返回正在监听名为 eventName的事件的监听器数量
      static listenerCount(emitter: EventEmitter, type: string | number): number;
      
      // 每个事件 默认可注册监听器的最大数量
      static defaultMaxListeners: number;
    
    
      // 修改事件 监听器数量的限制
      setMaxListeners(n: number): this;
      
      // 调用指定名称事件的 所有监听器
      emit(type: string | number, ...args: any[]): boolean;
      
      // 添加listener 函数到名为 type 的事件监听器数组末尾
      addListener(type: string | number, listener: Listener): this;
      // addListener 方法的别名
      on(type: string | number, listener: Listener): this;
      
      // 添加一个单次listener 函数到名为 eventName的事件。下次触发eventName事件时,监听器会被移除,然后调用
      once(type: string | number, listener: Listener): this;
      
      // 从type 的事件监听器数组中移除指定的 listener
      removeListener(type: string | number, listener: Listener): this;
      
      // 移除全部或指定type的监听器
      removeAllListeners(type?: string | number): this;
      
      // 返回type事件下的监听器数组
      listeners(type: string | number): Listener[];
      
      // 返回type事件下监听器数量
      listenerCount(type: string | number): number;
    }

    先来讲一个简单的实例

    EventEmitter的实例,绑定一个监听器。用eventEmitter.on()的方法来进行注册一个监听器,eventEmitter.emit()方法来触发引用事件。

    //引入events模块
    const EventEmitter = require('events');
    
    //创建一个新实例
    const myEmitter = new EventEmitter();
    //创建一个监听
    myEmitter.on('发工资了', () => {
      console.log('钱没了');
    });
    //触发监听事件
    myEmitter.emit('发工资了');
    

    eventEmitter.on()与eventEmitter.once()的区别,最明显的区别就是,他们后面的拼写不一样,着实在的的,其实是他们的运行,其中一个能够只要进行触发就会一直运行下去,触发一次运行一次,还有一个是不管你触发多少次,他只运行第一次。

    const myEmitter = new MyEmitter();
    let m = 0;
    myEmitter.once('event', () => {
      console.log(++m);
    });
    myEmitter.emit('event');
    // 打印: 1
    myEmitter.emit('event');
    // 不触发

    上面说了on和once的区别,现在又来了一个addListener,同样是添加监听事件的东西来看下他的使用方法

    emitter.addListeners("有钱", ()=>{
      console.log("买车")
    
    })
    emitter.emit('有钱');
    //1111
    

      可能细心的人发现了,这个方法和上面的又有什么分别呢,我能告诉,没有分别,就是拼的不一样罢了,来验证一下

    console.log(emitter.on == emitter.addListener)
    //true

    eventEmitter.on()之外还有另外的一种就是eventEmitter.once()的方法,其实这两个东西的区别用上面的代码看还真的没有什么多大的区别,但把他们拆分出底层来真的区别挺大的,完全就是两个东西来看下拆分区别

      区别

    addListener监听事件

    // 这里我们模拟 EventEmitter 类的定义实现了一个简单的具有添加事件监听器和触发事件的类
    class MyEmitter {
        // 首先初始化对象的时候创建了一个事件的容器
        constructor () {
            this.eventsPool = {}
        }
        
        // 添加事件监听器方法 将监听器添加到指定类型下的数组中
        addListener (type, listener) {
            if (!this.eventsPool) {
                this.eventsPool = {}
            }
            if(!this.eventsPool[type]){
                this.eventsPool[type]=[]
            }
            this.eventsPool[type].push(listener)
        }
        
        // 触发事件方法 遍历类型下所有事件监听器并调用
        emit (type, ...args) {
            let listeners = this.eventsPool[type]
            if (listeners) {
                for (let listener of listeners) {
                    listener(args)
                }
            }
       }
    }
    myEmitter.addListener('once', function() {
      console.log('once')
    })
     myEmitter.emit('once')
    

      

     once事件 

    class MyEmitter {
      // 首先初始化对象的时候创建了一个事件的容器
      constructor () {
          this.eventsPool = {}
      }
      
      // 添加事件监听器方法 将监听器添加到指定类型下的数组中
      once (type, listener) {
          if (!this.eventsPool) {
              this.eventsPool = {}
          }
          if(!this.eventsPool[type]){
              this.eventsPool[type]=[]
          }
          // >>> once 对listener 进行改造
              const onceListener = (...args) => {
                  let listeners = this.eventsPool[type]
                  let lIndex = listeners.findIndex(listener => listener === onceListener)
                  if (lIndex !== -1) {
                      listeners.splice(lIndex, 1)  
                  }
                  console.log("asfasf")
                  listener.apply(this, args)
              }
              
                  
          // >>>
          
          // >>> 将改造好的listener 放入监听器数组
          this.eventsPool[type].push(onceListener) 
            // console.log(this.eventsPool[type])
      }
      
      // 触发事件方法 遍历类型下所有事件监听器并调用
      emit (type, ...args) {
          let listeners = this.eventsPool[type]
          if (listeners) {
              for (let listener of listeners) {
                  listener(args)
              }
          }
     }
    }
    
    const myEmitter = new MyEmitter()
    myEmitter.once('once', function() {
        console.log('once11111')
    })
     myEmitter.emit('once')
    

      

      

     

    EventEmitter 会按照监听器注册的顺序同步地调用所有监听器。 所以必须确保事件的排序正确,且避免竞态条件。 可以使用 setImmediate() 或 process.nextTick() 切换到异步模式:(规范,第一个值是错误,第二个是值)区别→→

    const myEmitter = new MyEmitter();
    myEmitter.on('event', (a, b) => {
      setImmediate(() => {
        console.log('异步进行');
      });
    });
    myEmitter.emit('event', 'a', 'b');
    

     开始的时候我以为他的竞态事件是他内部的竞争关系,也就是下面这样,其实这种理解是有误的

    const myEmitter = new MyEmitter();
    myEmitter.on('event', (a, b) => {
      console.log(111)
      setImmediate(() => {
        console.log('异步进行');
      });
      console.log(222)
    });
    myEmitter.emit('event', 'a', 'b');
    
    //111
    //222
    //异步进行
    

      他这里避免竞态关系是避免监听事件的竞态关系

    const myEmitter = new MyEmitter();
    myEmitter.on('event', (a, b) => {
      console.log(111)
    });
    myEmitter.on('event', (a, b) => {
      setImmediate(() => {
        console.log('异步进行');
      });
    });myEmitter.on('event', (a, b) => {
      console.log(222)
    });
    myEmitter.emit('event', 'a', 'b');
    
    //111
    //222
    //异步进行
    

      虽然输出的结果是一样的,但是他这个避免竞态关系,确确实实是给监听事件的,而不是给输出事件的

     

     

      其实node的实例,在每个方法下都有限制要注册多少个监听器的,可以用

    EventEmitter.defaultMaxListeners来查看下,还可以通过更改他的数值来限定用多少个监听器。
    console.log(EventEmitter.defaultMaxListeners)
    //默认值10
    

      侦听器太多,可能导致内存泄漏,所以存在这样一个警告

    // 设置事件最大监听个数 
    girl.setMaxListeners(2); 
    girl.on("失恋", () => console.log("哭了")); 
    girl.on("失恋", () => console.log("喝酒"));
     girl.on("失恋", () => console.log("吸烟")); 
    girl.emit("失恋"); 
    // MaxListenersExceededWarning: 3 失恋 listeners added
     // 哭了 
    // 喝酒 
    // 吸烟
    

      

      

      现在添加监听的事件有了,来看看删除监听事件

    const EventEmitter = require('events');
    const myEE = new EventEmitter();
    const sss = () =>{
      console.log("aaa")
    }
    myEE.on('foo', sss);
    myEE.on('bar', () => {});
    
    myEE.removeListener('foo', sss)
    
    console.log(myEE.listeners('foo'))
    //[]
    

      上面的例子就是删除掉了foo监听里的sss事件,看上面了例子发现一个不认识的方法,那就是console.log内的方法myEE.listeners(name)这又是啥东西,为啥打印他就能知道他方法内有没有删除呢

    这个东西吧,其实就是node.js里面给的一种方法,他返回的是事件监听器里的副本,也就是相当于方法之类的。和他对应的还有一个那就是myEE.listenerCount(name) 他返回的就是这个事件中有多少的监听数量。

    const EventEmitter = require('events');
    const myEE = new EventEmitter();
    myEE.on('bar', () => {});
    myEE.on('bar', () => {});
    myEE.on('bar', () => {});
    myEE.listenerCount('bar')
    console.log(myEE.listenerCount('bar'))
    //3
    myEE.listeners('foo')
    console.log(myEE.listeners('bar'))
    //[ [Function], [Function], [Function] ]
    

      

     如过他要是多个不重名的方法应该怎么办呢,要怎么把他查出来,那就要用到这个方法了myEE.eventNames()    这个方法他能够直接的反应你代码用都有那些方法。

    const EventEmitter = require('events');
    const myEE = new EventEmitter();
    const sss = () =>{
      console.log("aaa")
    }
    myEE.on('foo', sss);
    myEE.on('bar', () => {});
    console.log(myEE.eventNames());
    //[ 'foo', 'bar' ]
    

      

    扯的有点远了,回到刚才,移除监听事件上面的移除事件,是根据条件来进行移除某一条,某一个监听的,下面我们来把所有的监听全部干掉。

    const EventEmitter = require('events');
    const myEE = new EventEmitter();
    const sss = () =>{
      console.log("aaa")
    }
    myEE.on('foo', sss);
    myEE.on('bar', () => {});
    
    myEE.removeAllListeners()
    

      这样你不管console.log哪个监听他都是空的

    使用prependListener添加事件并执行
    var EventEmitter = require("events")
    const girl = new EventEmitter();
    girl.on("失恋", () => console.log("哭了"));
    girl.prependListener("失恋", () => console.log("喝酒"));
    
    girl.emit("失恋");
    

      



    这里还有一个比较有意思的,看下案例
    const myEmitter = new MyEmitter();
    
    const callbackA = () => {
      console.log('A');
      myEmitter.removeListener('event', callbackB);
    };
    
    const callbackB = () => {
      console.log('B');
    };
    
    myEmitter.on('event', callbackA);
    
    myEmitter.on('event', callbackB);
    
    myEmitter.emit('event');
    //????
    
    myEmitter.emit('event');
    //???
    // callbackA 移除了监听器 callbackB,但它依然会被调用。
    // 触发时内部的监听器数组为 [callbackA, callbackB]
     

    EventEmitter 总结

    events 模块在 NodeJS 中的使用率非常高,很多其他模块的事件执行机制都是通过继承该模块的 EventEmitter 类来实现的,比如 ReadStream(可读流)、WriteStream(可写流)、net(tcp)和 http 等等,我们也可以通过上面案例的方式创建自己的类去继承 EventEmitter 来实现事件的管理。

    
    
    
  • 相关阅读:
    Spring事务原理分析-部分二
    Spring事务原理分析-部分一
    Spring 通读官方文档
    Spring IOC 源码解析
    SpringAOP原理分析
    Docker学习笔记
    TCP、UDP和HTTP关系
    洛谷P3312
    洛谷P3327
    BZOJ3073
  • 原文地址:https://www.cnblogs.com/yishifuping/p/9787536.html
Copyright © 2011-2022 走看看