zoukankan      html  css  js  c++  java
  • Nodejs学习笔记(二)—事件模块

    一.简介及资料

       http://nodejs.org/api/events.html

       http://www.infoq.com/cn/articles/tyq-nodejs-event

            events是node.js 最重要的模块,events模块只提供了一个对象events.EventEmitter,EventEmitter 的核心是事件发射与事件监听器。

            Node.js中大部分的模块,都继承自Event模块。

            与DOM树上事件不同,不存在事件冒泡、逐层捕获等行为。

            EventEmitter 支持若干个事件监听器。当事件发射时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。   

            如何访问:require('events');

    二.emitter.on(event, listener)

    /* 
        调用events模块,获取events.EventEmitter对象 
    */  
    var EventEmitter = require('events').EventEmitter;     
    var ee = new EventEmitter();  
      
    /* 
        EventEmitter.on(event, listener) 为事件注册一个监听 
        参数1:event  字符串,事件名 
        参数2:回调函数 
    */  
    ee.on('some_events', function(foo, bar) {  
        console.log("第1个监听事件,参数foo=" + foo + ",bar="+bar );  
    });  
      
    console.log('');  
    ee.emit('some_events', 'Wilson', 'Zhong');  
      
    console.log('第二轮');  
    ee.emit('some_events', 'Wilson', 'Z'); 

      在Sublime中,直接Ctrl+B即运行,如下所示:

            当然,也可以在cmd窗口中执行,如下所示 :

    三.emitter.emit(event, [arg1], [arg2], [...])

    var EventEmitter = require('events').EventEmitter;     
    var ee = new EventEmitter();  
      
    ee.on('some_events', function(foo, bar) {           
        console.log("第1个监听事件,参数foo=" + foo + ",bar="+bar );  
    });  
      
    /* 
        EventEmitter.emit(event, [arg1], [arg2], [...])   触发指定事件 
        参数1:event  字符串,事件名 
        参数2:可选参数,按顺序传入回调函数的参数 
        返回值:该事件是否有监听 
    */  
    var isSuccess = ee.emit('some_events', 'Wilson', 'Zhong');  
      
    ee.on('some_events', function(foo, bar) {           
        console.log("第2个监听事件,参数foo=" + foo + ",bar="+bar );  
    });  
      
    ee.emit('some_events', 'zhong', 'wei');  
      
    var isSuccess2 = ee.emit('other_events', 'Wilson', 'Zhong');  
      
    console.log(isSuccess);  
    console.log(isSuccess2);  

            示例进行了三次触发事件操作,其中some_events注册了监听,调用时emit函数会返回一个true,而other_events并没有注册监听,emit函数会返回一个false,表示该事件没有监听;当然也可以不用管这个返回值!

    四.emitter.once(event, listener)

    var EventEmitter = require('events').EventEmitter;     
    var ee = new EventEmitter();  
      
    /* 
        EventEmitter.once(event, listener)  为事件注册一次性监听,触发一次后移除监听 
        参数1:event  字符串,事件名 
        参数2:回调函数 
    */  
    ee.once('some_events', function(foo, bar) {  
        console.log("第1个监听事件,参数foo=" + foo + ",bar="+bar );  
    });  
      
      
    console.log('第一轮');  
    ee.emit('some_events', 'Wilson', 'Zhong');  
      
    console.log('第二轮');  
    var isSuccess =  ee.emit('some_events', 'Wilson', 'Zhong');  
    console.log(isSuccess);  

            从上面示例代码执行结果可以看出,用emitter.once给some_events注册一个监听后,分两轮调用emitter.emit触发,第二轮会返回false;这表示用emitter.once注册监听和用前面讲的emitter.on注册监听略有不同,emitter.once注册监听是一次性监听,当触发一次后,会移除该监听!当然,从名字上就看就比较明显了^_^!

    五.emitter.removeListener(event, listener)

            先来看一个失败的场景~~~

    var EventEmitter = require('events').EventEmitter;     
    var ee = new EventEmitter();  
      
    ee.on('some_events', function(foo, bar) {  
        console.log("第1个监听事件,参数foo=" + foo + ",bar="+bar );  
    });  
      
    /* 
        看到API中removeListener移除方法时,以为应该是这样 
        但是结果^_^!!!!! 
    */  
    ee.removeListener('some_events', function(){  
        console.log('成功移除事件some_events监听!');          
    });  
      
    console.log('第一轮');  
    ee.emit('some_events', 'Wilson', 'Zhong');  

            当我用emitter.on给some_events注册了一个监听后,我用emiiter.removeListener移除some_events的监听,随后再调用emitter.emit去触发,最后发现不是按我想像的在进行!为什么呢?

            我理所当然的认为emiiter.removeListener第二个参数是个回调函数,API还是要认真看清楚啊!!!

            下面再看个成功的场景~~~

    var EventEmitter = require('events').EventEmitter;     
    var ee = new EventEmitter();  
      
    var listener = function(foo,bar)  
    {  
        console.log("第1个监听事件,参数foo=" + foo + ",bar="+bar );  
    }  
      
    var listener2= function(foo,bar)  
    {  
        console.log("第2个监听事件,参数foo=" + foo + ",bar="+bar );  
    }  
      
    var listener3= function(foo,bar)  
    {  
        console.log("第3个监听事件,参数foo=" + foo + ",bar="+bar );  
    }  
      
    ee.on('some_events', listener);  
      
    ee.on('some_events', listener2);  
      
    ee.on('some_events', listener3);  
    /* 
        EventEmitter.removeListener(event, listener)  移除指定事件的监听器 
        注意:该监听器必须是注册过的 
        PS:上一个例子之后以会失败,很大原因就是忽略了监听器,理所当然的认为传个事件名就OK了,所以就悲剧了! 
    */  
    ee.removeListener('some_events', listener);  
      
    ee.removeListener('some_events', listener3);  
      
    ee.emit('some_events', 'Wilson', 'Zhong'); 

            我用示例中写法,给some_events添加了三个监听,又移除了第一个和第三个监听,最后再用emitter.emit触发some_events,输出结果不难发现,用emitter.removeListener移除的第一个和第三个监听都没有再起作用,

            想当然是害人地,原来emitter.removeListener的第二个参数是要移除的监听,而非移除成功后的回调函数……^_^!

    六.emitter.removeAllListeners([event])

            emitter.removeListener用过了,但一个事件可以有多个监听,需要全部移除时,一个个移除明显不是愉快的做法,不符合偷懒的天性!

            让我们来体验一下emitter.removeAllListeners带来的便捷!

    var EventEmitter = require('events').EventEmitter;     
    var ee = new EventEmitter();  
      
    var listener = function(foo,bar)  
    {  
        console.log("第1个监听事件,参数foo=" + foo + ",bar="+bar );  
    }  
      
    var listener2= function(foo,bar)  
    {  
        console.log("第2个监听事件,参数foo=" + foo + ",bar="+bar );  
    }  
      
    ee.on('some_events', listener);  
      
    ee.on('some_events', listener2);  
      
    ee.on('other_events',function(foo,bar)  
    {  
        console.log("其它监听事件,参数foo=" + foo + ",bar="+bar );  
    });  
      
    /* 
        EventEmitter.removeAllListeners([event])   移除(批定事件)所有监听器 
        参数1:可选参数,event  字符串,事件名 
    */  
    ee.removeAllListeners('some_events');  
      
    ee.emit('some_events', 'Wilson', 'Zhong');  
      
    ee.emit('other_events', 'Wilson', 'Zhong');  

            看看上面的执行结果,你会发现给some_events注册了两个监听;给other_events注册了一个监听;我调用emitter.removeAllListeners传了some_events事件名;

            最后使用emitter.on函数触发some_events和other_events两个事件,最后发现some_events注册的两个监听都不存在,而other_events注册的监听还存在;

            这表示当 emitter.removeAllListeners传用事件名作为参数时,为移除传入事件名的所有监听,而不会影响其它事件监听!

            emitter.removeAllListeners可以不传用事件名参数,直接执行。

    var EventEmitter = require('events').EventEmitter;     
    var ee = new EventEmitter();  
      
    var listener = function(foo,bar)  
    {  
        console.log("第1个监听事件,参数foo=" + foo + ",bar="+bar );  
    }  
      
    var listener2= function(foo,bar)  
    {  
        console.log("第2个监听事件,参数foo=" + foo + ",bar="+bar );  
    }  
      
    ee.on('some_events', listener);  
      
    ee.on('some_events', listener2);  
      
    ee.on('other_events',function(foo,bar)  
    {  
        console.log("其它监听事件,参数foo=" + foo + ",bar="+bar );  
    });  
      
    /* 
        EventEmitter.removeAllListeners([event])   移除(批定事件)所有监听器 
        参数1:可选参数,event  字符串,事件名 
    */  
    ee.removeAllListeners();  
      
    ee.emit('some_events', 'Wilson', 'Zhong');  
      
    ee.emit('other_events', 'Wilson', 'Zhong');  

            示例代码和传入参数时几乎一样,只是在调用emitter.removeAllListeners并没有传入指定事件名;

            运行结果会发现some_events和other_events所有监听都不存在了,它会移除所有监听!(比较暴力的方法一般要慎用~~)

    七.emitter.listeners(event)

    var EventEmitter = require('events').EventEmitter;     
    var ee = new EventEmitter();  
      
    var listener = function(foo,bar)  
    {  
        console.log("第1个监听事件,参数foo=" + foo + ",bar="+bar );  
    }  
      
    var listener2= function(foo,bar)  
    {  
        console.log("第2个监听事件,参数foo=" + foo + ",bar="+bar );  
    }  
      
    ee.on('some_events', listener);  
      
    ee.on('some_events', listener2);  
      
    ee.on('other_events',function(foo,bar)  
    {  
        console.log("其它监听事件,参数foo=" + foo + ",bar="+bar );  
    });  
      
    /* 
        EventEmitter.listeners(event)   //返回指定事件的监听数组 
        参数1:event  字符串,事件名     
    */  
    var listenerEventsArr = ee.listeners('some_events');  
      
    console.log(listenerEventsArr.length)  
      
    for (var i = listenerEventsArr.length - 1; i >= 0; i--) {  
        console.log(listenerEventsArr[i]);   
    };  

            给some_events注册两个监听,调用emitter.listeners函数,传入some_events事件名,接收函数返回值;

            从结果可以看出,返回值接收到some_events所有注册监听的集合!

    八.emitter.setMaxListeners(n)

            一个事件可以添加多个监听是没错,但Nodejs默认最大值是多少呢?

    var EventEmitter = require('events').EventEmitter;     
    var ee = new EventEmitter();  
      
    /* 
         给EventEmitter 添加11个监听 
    */  
    for (var i = 10; i >= 0; i--) {  
        ee.on('some_events',function()  
        {  
            console.log('第'+ (i +1) +'个监听');  
        });  
    };

            上面示例中我用个循环给some_events添加11个监听,执行代码,发现warning信息出现,并且提示的比较详细了,需要用emitter.setMaxListeners()去提升限值。

    var EventEmitter = require('events').EventEmitter;     
    var ee = new EventEmitter();  
      
    /* 
        EventEmitter.setMaxListeners (n)   给EventEmitter设置最大监听 
        参数1: n 数字类型,最大监听数 
         
        超过10个监听时,不设置EventEmitter的最大监听数会提示: 
        (node) warning: possible EventEmitter memory leak detected. 11 listeners added. 
         Use emitter.setMaxListeners() to increase limit. 
        设计者认为侦听器太多,可能导致内存泄漏,所以存在这样一个警告 
    */  
    ee.setMaxListeners(15);  
      
    /* 
         给EventEmitter 添加11个监听 
    */  
    for (var i = 10; i >= 0; i--) {  
        ee.on('some_events',function()  
        {  
            console.log('第'+ (i +1) +'个监听');  
        });  
    };  

            当我调用emitter.setMaxListeners传入15时,执行代码,warning信息不再出现;

            emitter.setMaxListeners的作用是给EventEmitter设置最大监听数,感觉一般是不需要设置这个值,10个还不够用的情况应该是比较少了!

            设计者认为侦听器太多会导致内存泄漏,所有就给出了一个警告!

    九.其它

            用的比较少的就不详细说了

    1.EventEmitter.defaultMaxListeners

            EventEmitter.defaultMaxListeners功能与setMaxListeners类似,给所有EventEmitter设置最大监听

    setMaxListeners优先级大于defaultMaxListeners。

    2.EventEmitter.listenerCount(emitter, event)

            返回指定事件的监听数

    3.特殊的事件Error

            引用自Node.js开发指南:EventEmitter 定义了一个特殊的事件 error,它包含了“错误”的语义,我们在遇到 异常的时候通常会发射 error 事件。当 error 被发射时,EventEmitter 规定如果没有响 应的监听器,Node.js 会把它当作异常,退出程序并打印调用栈。我们一般要为会发射 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。

    4.事件的继承

            有兴趣的可以自已看看:http://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor

    此系列的源代码可到http://bijian1013.iteye.com/blog/2425085下载。 

    文章来源:https://www.cnblogs.com/zhongweiv/p/nodejs_events.html

  • 相关阅读:
    反射
    IO流
    集合(下)
    集合(上)
    泛型
    异常
    常用类
    内部类
    将博客搬至CSDN
    DBMS_ERRLOG记录DML错误日志(二)
  • 原文地址:https://www.cnblogs.com/flyingeagle/p/9192507.html
Copyright © 2011-2022 走看看