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.
var Waiter = function() {
var dfd = []; // Placeholder for waiting objects
var doneArr = []; // Container for success callbacks
var failArr = []; // Container for failure callbacks
// Monitoring object class
var Promise = function() {
this.resolved = false; // Monitoring the successful resolution status of an object
this.rejected = false; // Monitoring the failure resolution status of an object
}
Promise.prototype = {
// Successful resolution
resolve: function() {
this.resolved = true; // Sets the current monitoring status to successful
if (!dfd.length) return void 0;
for (var i = dfd.length - 1; i >= 0; i--) {
// Iterates through monitoring objects, returning if any of them are not resolved or are rejected
if (dfd[i] && !dfd[i].resolved || dfd[i].rejected) return void 0;
dfd.splice(i, 1);
}
_exec(doneArr);
},
// Failure resolution
reject: function() {
this.rejected = true; // Sets the current monitoring status to failed
if (!dfd.length) return void 0; // No monitoring objects to cancel
dfd.splice(0); // Clears the monitoring objects
_exec(failArr);
}
}
this.Deferred = function() {
return new Promise();
};
// Callback execution method
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);
}
}
};
// Monitor asynchronous method parameters
this.when = function(...args) {
// Sets the monitoring objects
dfd = args;
var i = args.length;
// Iterates through the monitoring objects in reverse
for (--i; i >= 0; i--) {
// If the object does not exist, is already resolved, is rejected, or is not an instance of Promise
if (!args[i] || args[i].resolved || args[i].rejected || !args[i] instanceof Promise) {
args.splice(i, 1)
}
}
return this; // Returns the waiter object
};
// Adds successful resolution callback functions
this.done = function(...args) {
doneArr = doneArr.concat(args); // Adds callback methods to the success callback function container
return this;
};
```javascript
// To solve the failure callback function addition method
this.fail = function(...args) {
failArr = failArr.concat(args); // Add methods to the failure callback function
return this;
};
}
;(function(){
var waiter = new Waiter(); // Create a waiter instance
var first = function() {
var promise = waiter.Deferred();
setTimeout(() => {
promise.resolve();
}, 1000);
return promise; // Return the listening object
}();
var second = function() { // The second object
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(); // Create a waiter instance
var first = function() {
var promise = waiter.Deferred();
setTimeout(() => {
promise.resolve();
}, 1000);
return promise; // Return the listening object
}();
var second = function() { // The second object
var promise = waiter.Deferred();
setTimeout(() => {
promise.resolve();
}, 3000);
return promise;
}();
waiter.when(first, second).done(() => {
throw new Error("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 = 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); // 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
})
https://github.com/WindrunnerMax/EveryDay
https://juejin.cn/post/6844903645855612942
https://segmentfault.com/a/1190000021413444
https://www.cnblogs.com/hsp-blog/p/5889842.html