The Waiter Pattern is a technique that involves monitoring multiple asynchronous tasks and triggering future actions when these tasks are completed. This concept has been present even before the introduction of the Promise model. The difference lies in the fact that it wasn't standardized as a specific technical specification. The Waiter Pattern does not fall within the traditional definition of the 23 design patterns. It is generally regarded as a broad-spectrum, technical design pattern.
The Waiter Pattern involves listening for asynchronous processes to trigger future actions. For instance, in a scenario where operations A and B need to be completed before initiating operation C, this pattern proves useful. In software development, it's quite common to have dependencies on the completion of a prior operation before triggering the next one. In the context of JavaScript being single-threaded and unable to adopt a blocking approach, the conventional method for handling this was by using callbacks before the introduction of the Promise specification. However, this often led to the issue of callback hell. The Waiter Pattern can be seen as a precursor to the Promise specification, serving as a similar solution prior to its standardization.
varWaiter=function(){var dfd =[];// Placeholder for waiting objectsvar doneArr =[];// Container for success callbacksvar failArr =[];// Container for failure callbacks// Monitoring object classvarPromise=function(){this.resolved =false;// Monitoring the successful resolution status of an objectthis.rejected =false;// Monitoring the failure resolution status of an object}Promise.prototype ={// Successful resolutionresolve:function(){this.resolved =true;// Sets the current monitoring status to successfulif(!dfd.length)returnvoid0;for(var i = dfd.length -1; i >=0; i--){// Iterates through monitoring objects, returning if any of them are not resolved or are rejectedif(dfd[i]&&!dfd[i].resolved || dfd[i].rejected)returnvoid0; dfd.splice(i,1);}_exec(doneArr);},// Failure resolutionreject:function(){this.rejected =true;// Sets the current monitoring status to failedif(!dfd.length)returnvoid0;// No monitoring objects to cancel dfd.splice(0);// Clears the monitoring objects_exec(failArr);}}this.Deferred=function(){returnnewPromise();};// Callback execution methodfunction_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);}}};// Monitor asynchronous method parametersthis.when=function(...args){// Sets the monitoring objects dfd = args;var i = args.length;// Iterates through the monitoring objects in reversefor(--i; i >=0; i--){// If the object does not exist, is already resolved, is rejected, or is not an instance of Promiseif(!args[i]|| args[i].resolved || args[i].rejected ||!args[i]instanceofPromise){ args.splice(i,1)}}returnthis;// Returns the waiter object};// Adds successful resolution callback functionsthis.done=function(...args){ doneArr = doneArr.concat(args);// Adds callback methods to the success callback function containerreturnthis;};```javascript
// To solve the failure callback function addition methodthis.fail=function(...args){ failArr = failArr.concat(args);// Add methods to the failure callback functionreturnthis;};};(function(){var waiter =newWaiter();// Create a waiter instancevarfirst=function(){var promise = waiter.Deferred();setTimeout(()=>{ promise.resolve();},1000);return promise;// Return the listening object}();varsecond=function(){// The second objectvar promise = waiter.Deferred();setTimeout(()=>{ promise.resolve();},2000);return promise;}(); waiter.when(first, second).done(()=>{ console.log("success");}).fail(()=>{ console.log("fail");})})();;(function(){var waiter =newWaiter();// Create a waiter instancevarfirst=function(){var promise = waiter.Deferred();setTimeout(()=>{ promise.resolve();},1000);return promise;// Return the listening object}();varsecond=function(){// The second objectvar promise = waiter.Deferred();setTimeout(()=>{ promise.resolve();},3000);return promise;}(); waiter.when(first, second).done(()=>{thrownewError("test");}).fail(()=>{ console.log("fail");})})();
Promise is a solution for asynchronous operations, used to represent the final completion or failure of an asynchronous operation and its resulting value. Promise has various open-source implementations, and it is uniformly standardized in ES6, directly supported by browsers. The waiter pattern we implemented above is more similar to Promise.all().
This method returns a new promise object. The promise object will only trigger success when all the promise objects in the iterable parameter object are successful. Once any promise object in the iterable fails, the promise object is triggered for failure immediately. After the new promise object is triggered for success, it will return an array containing the return values of all promises in iterable as the return value of the success callback, in the order consistent with iterable. If the new promise object is triggered for failure, it will use the error information of the first failed promise object in iterable as its failure error information. The Promise.all method is commonly used to handle the status collection of multiple promise objects.
var p1 =newPromise((resolve, reject)=>{resolve("success1");})var p2 =newPromise((resolve, reject)=>{resolve("success2");})var p3 =newPromise((resolve, reject)=>{reject("fail");})Promise.all([p1, p2]).then((result)=>{ console.log(result);// Success status // ["success1", "success2"]}).catch((error)=>{ console.log(error);})Promise.all([p1,p3,p2]).then((result)=>{ console.log(result);}).catch((error)=>{ console.log(error);// Failure status // fail})