# 等待者模式 等待者模式是通过对多个异步任务进行监听,当异步任务完成后触发未来发生的动作,在没有`Promise`这个模型的时候,其实就已经出现这样类似的技术方案,不同的只是没有定制为一个技术规范,等待者模式不属于一般定义的`23`种设计模式的范畴,而通常将其看作广义上的技巧型设计模式。 ## 概述 等待者模式就是通过对异步进程监听,来触发未来发生的动作,举个例子当异步进程操作`A、B`需要都完成以后才能进行`C`进程操作,在开发中经常会这样,需要等到上个操作完成或者知道上个操作完成才去触发下个操作,而`JavaScript`又是单线程的,不能采用阻塞的方式去处理,在`Promise`规范出现之前通常都是使用回调的方式实现,这样很容易造成回调地狱,等待者模式就是在`Promise`规范制定之前一个类似于`Promise`的解决方案,可以算是`Promise`规范的一个参考前身。 ### 实现 ```javascript 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`对象的状态集合。 ```javascript 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 ```