天天看点

等待者模式

等待者模式

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

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