zoukankan      html  css  js  c++  java
  • 【JavaScript】让事件支持先发布后订阅

    之前写过一个的事件管理器,就是普通的先订阅后发布模式。但实际场景中我们需要做到后订阅的也能收到发布的消息。比如我们关注微信公众号,还是能看到历史消息的。类似于qq离线消息,我先发给你,你登录了就能收到了。就是确保订阅该事件的方法都能被执行

     var eventManger = {
            cached: {},
            handlers: {},
            //类型,绑定事件 
            addHandler: function (type, handler) {
                if (typeof handler !== "function") return;
    
                if (typeof this.handlers[type] == "undefined") {
                    this.handlers[type] = [];
                }
                this.handlers[type].push(handler);
    
                if (this.cached[type] instanceof Array) {
                    //说明有缓存的 可以执行
                    handler.apply(null, this.cached[type]);
                }
            },
            removeHandler: function (type, handler) {
                var events = this.handlers[type];
                for (var i = 0, len = events.length; i < len; i++) {
                    if (events[i] == handler) {
                        events.splice(i, 1);
                        break;
                    }
                }
            },
            trigger: function (type) {
                //如果有订阅的事件,这个时候就触发了
                if (this.handlers[type] instanceof Array) {
                    var handlers = this.handlers[type];
                    var args = Array.prototype.slice.call(arguments, 1);
                    for (var i = 0, len = handlers.length; i < len; i++) {
                        handlers[i].apply(null, args);
                    }
                }
                //默认缓存
                this.cached[type] = Array.prototype.slice.call(arguments, 1);
            }
        };

    其实就是增加了几行代码。缓存下最后一次触发的时的参数。 然后在addhandle的时候进行判断,如果订阅的时候已经有缓存的参数了,说明该方法可以执行了。

    eventManger.addHandler("test", function (res) {
        console.log("先订阅,后发布1", res);
    })
    
    eventManger.trigger("test", 2);
    
    
    eventManger.addHandler("test", function (res) {
        console.log("先发布,后订阅2", res);
    })
    
    eventManger.addHandler("test", function (res) {
        console.log("先发布,后订阅3", res);
    })

    我实际的场景是这样的A事件触发之后,才能执行B方法。但B方法需要在C方法完成之后。也就是B依赖于A和C的完成。且A几乎每次都会很快触发,当然可以设两个个开关变量和一个代理函数,等两个事件都完成之后再do B。代码如下:

    var aReady = false;
    var cReady = false;
    eventManger.addHandler("A", function () {
        aReady = true;
        console.log("do A");
        proxyC();
    });
    
    eventManger.trigger("A", 2);
    
    function doB() {
        console.log("do B");
        //实际B中的方法需要在A事件成功之后才能执行
    }
    
    function doC() {
        console.log("do C");
        cReady = true;
        proxyC();
    }
    
    function proxyC() {
        aReady && cReady && doB();
    }
    doC();

    这样功能是实现了,但是可读性差了,而且事件订阅必须要对位置,如果在trigger之前,doB就永远执行不了,而且代码上多了两个变量和一个方法,最傻的是用一个变量加setTimeout去判断状态,这就可能陷入死循环。

    var aReady = false;
    eventManger.addHandler("A", function () {
        aReady = true;
        console.log("do A");
    });
    
    
    function doB() {
        console.log("do B");
        //实际B中的方法需要在A事件成功之后才能执行
    }
    
    function doC() {
        console.log("do C");
        if (!aReady) {
            console.log("wating...");
            setTimeout(doC, 50);
            return;
        }
        doB();
    }
    
    doC();
    
    eventManger.trigger("A", 2);//模拟A事件触发迟

    这种办法最不可取吧。因为外部事件可能挂掉,这儿就走不出去了。等于是挖了个坑。但如果事件支持先发布,后订阅,问题就简单了:

    eventManger.trigger("A", 2);
    
    function doB() {
        console.log("do B");
        //实际B中的方法需要在A事件成功之后才能执行
    }
    
    function doC() {
        console.log("do c");
        eventManger.addHandler("A", function () {
            console.log("do a");
            doB();
        });
    }
    doC();

     

    这样就清晰了很多。事件订阅也不必那么在意调用的位置了。以上只是记住最近的一次的调用参数,可以用于后订阅的事件触发。这适合一次性事件(一个周期只会触发一次的事件)。如果是像推送消息的事件,会不断的触发,如果想要确保也能获得全部的历史记录,就需要记住所有的参数。这是一种情况;实际可能还会有更多的流程依赖,当然对于流程控制有很多办法,也有很多库支持。比如promise和async。本文只是阐述了一个事件和方法的流程相关场景,也许对你有启发。

  • 相关阅读:
    .net URL加密和解密
    全面分析VB.NET文件传送
    如何美化你的.net 应用程序 (皮肤使用)
    获取机器的硬件信息(CPU ID序列号, 主板信息,硬盘序列号,系统信息)
    这是博客园的一个Bug吗?
    [转]深入理解JavaScript闭包(closure)
    【翻译】在ASP.NET中的DatePicker控件
    [翻译]ASP.NET MVC 2 Preview 1 发布了
    页面性能优化减少HTTP请求
    [翻译]C#闭包内幕(Looking Inside C# Closures)
  • 原文地址:https://www.cnblogs.com/stoneniqiu/p/6814468.html
Copyright © 2011-2022 走看看