天天看點

等待者模式

等待者模式

等待者模式是通過對多個異步任務進行監聽,當異步任務完成後觸發未來發生的動作,在沒有

Promise

這個模型的時候,其實就已經出現這樣類似的技術方案,不同的隻是沒有定制為一個技術規範,等待者模式不屬于一般定義的

23

種設計模式的範疇,而通常将其看作廣義上的技巧型設計模式。

描述

等待者模式就是通過對異步程序監聽,來觸發未來發生的動作,舉個例子當異步程序操作

A、B

需要都完成以後才能進行

C

程序操作,在開發中經常會這樣,需要等到上個操作完成或者知道上個操作完成才去觸發下個操作,而

JavaScript

又是單線程的,不能采用阻塞的方式去處理,在

Promise

規範出現之前通常都是使用回調的方式實作,這樣很容易造成回調地獄,等待者模式就是在

Promise

規範制定之前一個類似于

Promise

的解決方案,可以算是

Promise

規範的一個參考前身。

實作

var Waiter = function() {
    var dfd = []; // 等待對象容器
    var doneArr = []; // 成功回調容器
    var failArr = []; // 失敗回調容器

    //監控對象類
    var Promise = function() {
        this.resolved = false; // 監控對象是否解決成功狀态
        this.rejected = false; // 監控對象是否解決失敗狀态
    }

    Promise.prototype = {
        //解決成功
        resolve: function() {
            this.resolved = true; // 設定目前監控狀态是成功
            if (!dfd.length) return void 0;
            for (var i = dfd.length - 1; i >= 0; i--) {
                // 對象監控對象周遊如果任一個對象沒有解決或者失敗就傳回
                if (dfd[i] && !dfd[i].resolved || dfd[i].rejected) return void 0;
                dfd.splice(i, 1);
            }
            _exec(doneArr);
        },
        //解決失敗
        reject: function() {
            this.rejected = true; // 設定目前監控狀态是失敗
            if (!dfd.length) return void 0; // 沒有監控對象取消
            dfd.splice(0); // 清除監控對象
            _exec(failArr);
        }
    }

    this.Deferred = function() {
        return new Promise();
    };

    //回調執行方法
    function _exec(arr) {
        for (let i = 0, len = arr.length; i < len; i++) {
            try {
                arr[i] && arr[i]();
            } catch (e) {
                // console.warn("Error", e);
                _exec(failArr);
            }
        }
    };

    // 監控異步方法參數
    this.when = function(...args) {
        //設定監控對象
        dfd = args;
        var i = args.length;
        //向前周遊監控對象
        for (--i; i >= 0; i--) {
            //不存在監控對象 監控對象已經解決 監控對象失敗
            if (!args[i] || args[i].resolved || args[i].rejected || !args[i] instanceof Promise) {
                args.splice(i, 1)
            }
        }
        return this; // 傳回等待者對象
    };

    //解決成功回調函數添加方法
    this.done = function(...args) {
        doneArr = doneArr.concat(args); // 向成功毀掉函數容器中添加回調方法
        return this;
    };

    //解決失敗回調函數添加方法
    this.fail = function(...args) {
        failArr = failArr.concat(args); // 向失敗回調函數中添加方法
        return this;
    };
}

;(function(){
    var waiter = new Waiter(); // 建立一個等待者執行個體
    var first = function() {
        var promise = waiter.Deferred();
        setTimeout(() => {
            promise.resolve();
        }, 1000);
        return promise; // 傳回監聽這對象
    }();
    var second = function() { // 第二個對象
        var promise = waiter.Deferred();
        setTimeout(() =>  {
            promise.resolve();
        }, 2000);
        return promise;
    }();
    waiter.when(first, second).done(() => {
        console.log("success");
    }).fail(() => {
        console.log("fail");
    })
})();

;(function(){
    var waiter = new Waiter(); // 建立一個等待者執行個體
    var first = function() {
        var promise = waiter.Deferred();
        setTimeout(() => {
            promise.resolve();
        }, 1000);
        return promise; // 傳回監聽這對象
    }();
    var second = function() { // 第二個對象
        var promise = waiter.Deferred();
        setTimeout(() => {
            promise.resolve();
        }, 3000);
        return promise;
    }();
    waiter.when(first, second).done(() => {
        throw new Error("test");
    }).fail(() => {
        console.log("fail");
    })
})();
           

Promise

Promise

就是異步操作的一個解決方案,用于表示一個異步操作的最終完成或失敗及其結果值,

Promise

有各種開源實作,在

ES6

中被統一規範,由浏覽器直接支援。上面我們實作的等待者模式更類似于

Promise.all()

示例

這個方法傳回一個新的

promise

對象,該

promise

對象在

iterable

參數對象裡所有的

promise

對象都成功的時候才會觸發成功,一旦有任何一個

iterable

裡面的

promise

對象失敗則立即觸發該

promise

對象的失敗。這個新的

promise

對象在觸發成功狀态以後,會把一個包含

iterable

裡所有

promise

傳回值的數組作為成功回調的傳回值,順序跟

iterable

的順序保持一緻;如果這個新的

promise

對象觸發了失敗狀态,它會把

iterable

裡第一個觸發失敗的

promise

對象的錯誤資訊作為它的失敗錯誤資訊。

Promise.all

方法常被用于處理多個

promise

對象的狀态集合。

var p1 = new Promise((resolve, reject) => {
  resolve("success1");
})

var p2 = new Promise((resolve, reject) => {
  resolve("success2");
})

var p3 = new Promise((resolve, reject) => {
  reject("fail");
})

Promise.all([p1, p2]).then((result) => {
  console.log(result);     // 成功狀态 // ["success1", "success2"]
}).catch((error) => {
  console.log(error);
})

Promise.all([p1,p3,p2]).then((result) => {
  console.log(result);
}).catch((error) => {
  console.log(error);      // 失敗狀态 // fail
})
           

每日一題

https://github.com/WindrunnerMax/EveryDay
           

參考

https://juejin.cn/post/6844903645855612942
https://segmentfault.com/a/1190000021413444
https://www.cnblogs.com/hsp-blog/p/5889842.html