Ways to Write Promises by hand.
Abstract
Promise is an asynchronous solution for the JS community, providing developers with basic methods such as. Then (), promise.resolve (), promise.reject (). In addition, methods such as.all() and.race() are provided to make it easier to combine and control multiple Promise instances.
This article will implement a more advanced method manually on the basic method of Promise to deepen the understanding of Promise:
- 🤔 ️ implementation
Promise.all
- 🤔 ️ implementation
Promise.race
- 🤔 ️ implementation
Promise.any
- 🤔 ️ implementation
Promise.allSettled
- 🤔 ️ implementation
Promise.finally
⚠️ Full code and use cases can be found at github.com/dongyuanxin… .
Realize the Promise. All
process
Promise.all(iterators) returns a new Promise instance. Iterators contain multiple promise instances passed in from the outside world.
For a new Promise instance returned, there are two cases:
- If the state of all incoming Promise instances becomes
fulfilled
, the state of the returned Promise instance isfulfilled
, and its value is an array of all the promise values passed in. - If one of the Promise instances changes state
rejected
, the state of the returned Promise instance immediately changes torejected
.
Code implementation
Implementation idea:
- It doesn’t have to be an array object, it can be a traverser.
- Each instance passed in doesn’t have to be a promise
Promise.resolve()
packaging - With “counters”, flag whether all instances are in state
fulfilled
Promise.myAll = function(iterators) {
const promises = Array.from(iterators);
const num = promises.length;
const resolvedList = new Array(num);
let resolvedNum = 0;
return new Promise((resolve, reject) = > {
promises.forEach((promise, index) = > {
Promise.resolve(promise)
.then(value= > {
// Save the value of the Promise instance
resolvedList[index] = value;
// Through the counter, mark whether all instances are fulfilled
if (++resolvedNum === num) {
resolve(resolvedList);
}
})
.catch(reject);
});
});
};
Copy the code
Realize the Promise. Race
process
Promise.race(iterators) passes the same parameters and returns the same values as promise.all. However, the state and value of the returned promise instance completely depend on: Among all the promise instances passed in, the one that changes the state first (whether this is a pity or rejected).
Code implementation
Implementation idea:
- An incoming instance
pending -> fulfilled
, its value isPromise.race
Value of the returned Promise instance - An incoming instance
pending -> rejected
, its error isPromise.race
Error for the returned Promise instance
Promise.myRace = function(iterators) {
const promises = Array.from(iterators);
return new Promise((resolve, reject) = > {
promises.forEach((promise, index) = > {
Promise.resolve(promise)
.then(resolve)
.catch(reject);
});
});
};
Copy the code
Realize the Promise. Any
I write the front-end focused tech blog Xin-tan.com. You can access the Watch or Star articles repository at github.com/dongyuanxin… Or follow the public account “Xintan Blog” to receive the latest news of articles.
process
Promise.any(iterators) passes the same parameters and returns the same values as promise.all.
If any of the passed instances becomes a big pity, the state of the returned Promise instance will immediately become a big pity. If all instances change to Rejected, then it returns a Promise instance with the state Rejected.
⚠️ promise. all = promise. any = array.prototype. every = array.prototype. some
Code implementation
The implementation idea is similar to promise.all. This counter is used to indicate whether all instances of rejected are rejected because the processing logic for asynchronous processes is different.
Promise.any = function(iterators) {
const promises = Array.from(iterators);
const num = promises.length;
const rejectedList = new Array(num);
let rejectedNum = 0;
return new Promise((resolve, reject) = > {
promises.forEach((promise, index) = > {
Promise.resolve(promise)
.then(value= > resolve(value))
.catch(error= > {
rejectedList[index] = error;
if(++rejectedNum === num) { reject(rejectedList); }}); }); }); };Copy the code
Realize the Promise. AllSettled
process
Promise.allsettled (iterators) passes the same parameters and returns the same values as Promise.all.
According to ES2020, the state of this returned Promise instance can only be fulfilled. For all incoming Promise instances, it waits for each promise instance to end and returns the specified data format.
If you pass in two promise instances A and B, a changes to Rejected and the error is error1. This will be a pity, and value is 1. Then promise.allSettled returns the value of the Promise instance:
[{ status: "rejected".value: error1 }, { status: "fulfilled".value: 1 }];
Copy the code
Code implementation
Counters in the implementation that count all incoming promise instances.
const formatSettledResult = (success, value) = >
success
? { status: "fulfilled", value }
: { status: "rejected".reason: value };
Promise.allSettled = function(iterators) {
const promises = Array.from(iterators);
const num = promises.length;
const settledList = new Array(num);
let settledNum = 0;
return new Promise(resolve= > {
promises.forEach((promise, index) = > {
Promise.resolve(promise)
.then(value= > {
settledList[index] = formatSettledResult(true, value);
if (++settledNum === num) {
resolve(settledList);
}
})
.catch(error= > {
settledList[index] = formatSettledResult(false, error);
if(++settledNum === num) { resolve(settledList); }}); }); }); };Copy the code
Promise.all, promise. any, and promise. allSettled
These three methods all use counters for asynchronous process control. The following table compares the use of counters in different methods to enhance understanding:
The method name | use |
---|---|
Promise.all |
Mark the number of depressing instances |
Promise.any |
Indicates the number of instances of Rejected |
Promise.allSettled |
Mark the number of all instances (depressing and Rejected) |
Realize the Promise. Prototype. Finally
process
It is a syntactic sugar that fires after the current Promise instance executes either a THEN or a catch.
For example, a promise prints time stamps in both then and catch:
new Promise(resolve= > {
setTimeout((a)= > resolve(1), 1000);
})
.then(value= > console.log(Date.now()))
.catch(error= > console.log(Date.now()));
Copy the code
Now the common logic that must be executed in finally can be abbreviated as: finally
new Promise(resolve= > {
setTimeout((a)= > resolve(1), 1000);
}).finally((a)= > console.log(Date.now()));
Copy the code
As you can see, Promise. Prototype. Finally the execution has nothing to do with Promise instance state, does not depend on Promise after the execution of the returned result value. The argument passed in is a function object.
Code implementation
Implementation idea:
- Given that the Promise’s resolver may be an asynchronous function, the finally implementation adds callback logic by calling the then method on the instance
- Value transparently transmitted successfully, error transparently transmitted failed
Promise.prototype.finally = function(cb) {
return this.then(
value= > Promise.resolve(cb()).then((a)= > value),
error =>
Promise.resolve(cb()).then((a)= > {
throwerror; })); };Copy the code
Refer to the link
- The code and use cases are at: github.com/dongyuanxin…
- ECMAScript 6 Getting Started -Promise Objects
- Github.com/tc39/propos…
- Github.com/matthew-and…
If you feel you have gained something, welcome to Watch or Star articles warehouse github.com/dongyuanxin… , or scan the public account “Xintan Blog” to unlock more articles.