1. 程式人生 > >【JavaScript】讓事件支持先發布後訂閱

【JavaScript】讓事件支持先發布後訂閱

class 問題 想要 png trigger 很快 9.png area ++

之前寫過一個的事件管理器,就是普通的先訂閱後發布模式。但實際場景中我們需要做到後訂閱的也能收到發布的消息。比如我們關註微信公眾號,還是能看到歷史消息的。類似於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。本文只是闡述了一個事件和方法的流程相關場景,也許對你有啟發。

【JavaScript】讓事件支持先發布後訂閱