最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • [2] node模块事件 event -> EventEmitter

    正文概述 掘金(瑟曦爱唐僧)   2021-06-03   565

    [2] node模块事件 event  -> EventEmitter

    Node.js 中绝大多数模块都依赖于此模块,Net、HTTP、FS、Stream 等,除了这些系统模块比较知名的 ExpressKoa 框架中也能看到 EventEmitter

    1: on & emit & once

        const Events = require("events");
        class MyEvent extends Events {}
        var e1 = new MyEvent();
    
        e1.on("start", (name) => {
          console.log("start:event11 ", name,this === el);
        });
        e1.on("start", () => {
           console.log("start:event22 ");
        });
        
        // 只执行一次的注册函数
        el.once('eat:breskfast',function(){
            console.log('eat breakfast')
        })
    
        e1.emit("start", "anikin"); 
        
        // 执行结果是: 可以多次监听一次触发
        // console.log("start:event11 ", name,this === el);
        // console.log("start:event22 ");
        
        el.emit('eat:breskfast')
    

    注意区分ononce:

        var num = 0;
        e1.on("test", function () {
          console.log("test", ++num);
        });
    
        e1.emit("test");  //  test,1
        e1.emit("test");  //  test,2
        
        // 如果是once 
         e1.once("test", function () {
          console.log("test", ++num);
        });
        
        e1.emit("test");  //  test,1
        e1.emit("test");   //  test,1
        
        // 再次验证
        e1.prependOnceListener("test", function () {
          console.log("apend test", ++num);
        });
        
        e1.emit("apend test");  //   test,1
        e1.emit("test");   //  test,2
        
    

    prependOnceListener方法可用于将事件监听器添加到监听器数组的开头。下次调用的时候也会清楚自身的,不会多次调用。

    2: 异步或者同步

    EventEmitter 以注册的顺序同步地调用所有监听器。 这样可以确保事件的正确排序,并有助于避免竞态条件和逻辑错误。 当适当时,监听器函数可以使用 setImmediate()process.nextTick() 方法切换到异步的操作模式:

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

    3: error

    EventEmitter 实例出错时,应该触发 error事件。 这些在Node.js中被视为特殊情况。如果没有为 error事件注册监听器,则当 error事件触发时,会抛出错误、打印堆栈跟踪、并退出 Node.js 进程。

    const myEmitter = new MyEmitter();
    myEmitter.emit('error', new Error('错误信息'));
    

    4:解决高并发下的雪崩问题

    对于需要查询 DB 的数据,我们一般称之为热点数据,这类数据通常是要在 DB 之上增加一层缓存,但是在高并发场景下,如果这个缓存正好失效,此时就会有大量的请求直接涌入数据库,对数据库造成一定的压力,对于缓存雪崩的解决方案,网上也不乏有更好的解决方案,但是在 Node.js 中我们可以利用 events 模块提供的 once() 方法来解决。

    当触发多次相同名称事件,通过 once 添加的侦听器只会执行一次,并且在执行之后会接触与它关联的事件,相当于 on 方法和 removeListener 方法的组合,源码体现:

        function onceWrapper(...args) {
          if (!this.fired) {
            this.target.removeListener(this.type, this.wrapFn);
            this.fired = true;
            return Reflect.apply(this.listener, this.target, args);
          }
        }
    
        function _onceWrap(target, type, listener) {
          var state = { fired: false, wrapFn: undefined, target, type, listener };
          var wrapped = onceWrapper.bind(state);
          wrapped.listener = listener;
          state.wrapFn = wrapped;
          return wrapped;
        }
    
        EventEmitter.prototype.once = function once(type, listener) {
          checkListener(listener);
    
          this.on(type, _onceWrap(this, type, listener));
          return this;
        };
    

    利用 once 方法将所有请求的回调都压入事件队列中,对于相同的文件名称查询保证在同一个查询开始到结束的过程中永远只有一次,如果是 DB 查询也避免了重复数据带来的数据库查询开销。代码编写参考了深入浅出 Nodejs Events 模块一书,这里使用 fs 进行文件查询,如果是 DB 也同理,另外注意使用 status 键值对形式保存了触发/监听的事件名称和状态,最后建议进行清除,避免引起大对象导致内存泄露问题。

        const events = require('events');
        const emitter = new events.EventEmitter();
        const fs = require('fs');
        const status = {};
    
        const select = function(file, filename, cb) {
            emitter.once(file, cb);
    
            if (status[file] === undefined) {
                status[file] = 'ready'; // 不存在设置默认值
            }
            if (status[file] === 'ready') {
                status[file] = 'pending';
                fs.readFile(file, function(err, result) {
                    console.log(filename);
                    emitter.emit(file, err, result.toString());
                    status[file] = 'ready';
    
                    setTimeout(function() {
                        delete status[file];
                    }, 1000);
                });
            }
        }
    
        for (let i=1; i<=11; i++) {
            if (i % 2 === 0) {
                select(`/tmp/a.txt`, 'a 文件', function(err, result) {
                    console.log('err: ', err, 'result: ', result);
                });
            } else {
                select(`/tmp/b.txt`, 'b 文件', function(err, result) {
                    console.log('err: ', err, 'result: ', result);
                });
            }
        }
    

    其实上面代码比较繁琐,说简单点就是加了一个锁来实现。

    默认情况下,如果为特定事件添加了超过 10 个监听器,则 EventEmitter 会打印一个警告。 但是,并不是所有的事件都要限制 10 个监听器。 emitter.setMaxListeners() 方法可以为指定的 EventEmitter 实例修改限制。

    许多 Node.js 成功的模块和框架都是基于 EventEmitter 的,学会 EventEmitter 的使用,并且知道该在什么时候去使用是非常有用的。

    EventEmitter 本质上就是观察者模式的实现,一个类似的模式是发布/订阅,生产者将消息发布之后无需关心订阅者的实现,类似RabbitMQ 本身也是基于 AMQP 协议,这在一个分布式集群环境中使用也是非常好的一种方案。

    参考:

    nodejs.cn/api/events.…

    
    
    
    

    起源地下载网 » [2] node模块事件 event -> EventEmitter

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元