等待者模式 等待者模式

等待者模式是通过对多个异步任务进行监听,当异步任务完成后触发未来发生的动作,在没有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