preface
All, Promise.AllSettled, promise.race, and elegant implementation are the three most important static methods in Promise.
Promise.all
Promise.all in the current handwritten topic heat frequency should be top5 level, so we need to deeply grasp the promise. all method. Let’s start with a brief review of the All method.
Based on learning
Promise. All method is similar to a group of brothers walking side by side, parameters can be analogous to a group of brothers, only when all brothers are happy, all eldest will harvest happiness; As long as one brother is unhappy, the eldest is unhappy.
The promise.all () method is used to wrap multiple Promise instances into a new Promise instance.
const p = Promise.all([p1, p2, p3]);
Copy the code
The promise. all method takes an array of arguments, and p1, p2, and p3 are all Promise instances. If it is not a Promise instance, the promise.resolve method is first called to convert the parameter to a Promise instance before the next step is taken.
The status of return value P is determined by P1, P2 and P3, which can be divided into two cases:
-
Only when the states of P1, P2 and P3 become depressing, the state of P will become depressing. At this time, the return values of P1, P2 and P3 will form an array and be passed to the callback function of P.
-
If p1, P2, and P3 are rejected, P becomes rejected, and the return value of the first rejected instance is passed to p’s callback function.
// Emulate asynchronous promises
const p1 = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve(1);
}, 1000);
});
/ / promise
const p2 = Promise.resolve(2);
/ / constant value
const p3 = 3;
// Failed promise
const p4 = Promise.reject("error");
// Asynchronous failed promise
const p5 = new Promise((resolve, reject) = > {
setTimeout(() = > {
reject("TypeError");
}, 1000);
});
// 1
Promise.all([p1, p2, p3])
.then((data) = > console.log(data)) / / [1, 2, 3]
.catch((error) = > console.log(error));
// 2. There are failed promises
Promise.all([p1, p2, p3, p4])
.then((data) = > console.log(data))
.catch((error) = > console.log(error)); // error
// 3. Multiple failed promises exist
Promise.all([p1, p2, p3, p4, p5])
.then((data) = > console.log(data))
.catch((error) = > console.log(error)); // error
Copy the code
From the output of the above case, we can draw the following conclusions:
p
The status is determined by the execution result of the parameters. Success is returned if all the parameters are successful, and failure occurs if there is one failure- Parameters for the
Promise
Instance, will passPromise.resolve
Converted intoPromise
The instance - Success returns an array with the data sorted by parameters
- Short circuit effect: only the first failure message is returned
Iterator interface parameters
The ES6 Primer also points out that the promise. all method may not be an array, but it must have an Iterator interface and that each member returned is a Promise instance
Promise. All uses an Iterator type that requires iterators to be all Promise instances. Let’s use String as an example to see if promise. all can support iterating items as non-Promise instances.
// ['x', 'i', 'a', 'o', 'b', 'a', 'o']
Promise.all("xiaobao").then((data) = > console.log(data));
Copy the code
As you can see, promises treat the Iterator type the same way arrays do; if the argument is not a Promise instance, promise. all is called first to convert it to a Promise instance.
Thought analysis
Promise.all
Will return a new onePromise
object
Promise.all = function (promises) {
return new Promise((resolve, reject) = > {});
};
Copy the code
- (Bright spot)
all
Method arguments can be arrays, or they can beIterator
Type, and therefore should be usedfor of
Loop through.
Promise.all = function (promises) {
return new Promise((resolve, reject) = > {
for (let p of promises) {
}
});
};
Copy the code
- Some parameters may not be
Promise
Type, so the parameter is passed before being usedPromise.resolve
conversion
Promise.all = function (promises) {
return new Promise((resolve, reject) = > {
for (let p of promises) {
// Make sure all parameters are Promise instances, and then proceed
Promise.resolve(p).then((data) = > {
/ /...}); }}); };Copy the code
Iterator
Type we have no way of knowing the iteration depth, so we maintain onecount
Used to recordpromise
Total while maintainingfulfilledCount
Stands for accomplishedpromise
Number, whencount === fulfilledCount
Represents all incomingPromise
If the command is executed successfully, data is displayed.
Promise.all = function (promises) {
let count = 0; / / the total number of promise
let fulfilledCount = 0; // The number of completed promises
return new Promise((resolve, reject) = > {
for (let p of promises) {
count++; // Promise totals + 1
Promise.resolve(p).then((data) = > {
fulfilledCount++; // The number of fulfilled promises +1
if (count === fulfilledCount) {
// the last promise is completedresolve(); }}); }}); };Copy the code
Count === = all promises are fulfilled.
The promise. then method is microTasks, and the Event Loop will execute the microTasks after the synchronization task is completed. Count++ is in the sync code section, so you have successfully counted the total number of promises before executing the promise.then method.
Then the promise.then method is executed in turn, and ledcount is incremented. When count === ledCount is enabled, all promises have been successfully completed. 5. The order in which the data is returned should be the tricky part of the all method.
- Create an array
result
Store allpromise
Success data - in
for of
Loop, uselet
Variable definitionsi
, whose value is equal to the current traversal index let
The variable defined does not get variable promotion, so we just letresult[i]
为promise
Success data, so that you can output the results in the order of parameter input
Promise.all = function (promises) {
const result = []; // Store promise success data
let count = 0;
let fulfilledCount = 0;
return new Promise((resolve, reject) = > {
for (let p of promises) {
// I is the number of promises iterated
// Use let to avoid closure issues
let i = count;
count++;
// Make sure all parameters are Promise instances, and then proceed
Promise.resolve(p).then((data) = > {
fulfilledCount++;
// Assign the ith promise success data to the corresponding location
result[i] = data;
if (count === fulfilledCount) {
// the last promise is completed
// Returns the result arrayresolve(result); }}); }}); };Copy the code
- Let’s do the boundary case
- a
promise
Failure – direct callreject
Can be - The incoming
promise
The number of0
— Return an empty array (specification) - Code execution throws an exception — returns an error message
- a
// Redundant code is omitted
Promise.all = function (promises) {
return new Promise((resolve, reject) = > {
// 3. Catch exceptions in code execution
try{
for (let p of promises) {
Promise.resolve(p).then(data= > {}
.catch(reject); Call reject directly to return the failure cause})}// 2. The number of incoming promises is 0
if (count === 0) {
resolve(result)
}
} catch(error) {
reject(error)
}
})
}
Copy the code
The source code to achieve
Let’s put the above code together, add detailed comments, and test the success of handwritten promise.all.
Promise.all = function (promises) {
const result = []; // Store promise success data
let count = 0; / / the total number of promise
let fulfilledCount = 0; // Fulfill the promise number
return new Promise((resolve, reject) = > {
// Catch exceptions in code execution
try {
for (let p of promises) {
// I is the number of promises iterated
// Use let to avoid closure issues
let i = count;
count++; // Promise totals + 1
Promise.resolve(p)
.then((data) = > {
fulfilledCount++; // The number of fulfilled promises +1
// Assign the ith promise success data to the corresponding location
result[i] = data;
if (count === fulfilledCount) {
// the last promise is completed
// Returns the result array
resolve(result);
}
})
.catch(reject);
// The number of incoming promises is 0
if (count === 0) {
resolve(result); // Returns an empty array}}}catch(error) { reject(error); }}); };Copy the code
Test code (use the test code in the case, with the Iterator type Stirng attached):
// 1
Promise.all([p1, p2, p3])
.then((data) = > console.log(data)) / / [1, 2, 3]
.catch((error) = > console.log(error));
// 2. There are failed promises
Promise.all([p1, p2, p3, p4])
.then((data) = > console.log(data))
.catch((error) = > console.log(error)); // error
// 3. Multiple failed promises exist
Promise.all([p1, p2, p3, p4, p5])
.then((data) = > console.log(data))
.catch((error) = > console.log(error)); // error
// 4. String type
Promise.all("zcxiaobao").then((data) = > console.log(data));
// ['z', 'c', 'x', 'i', 'a', 'o', 'b', 'a', 'o']
Copy the code
Promise.allSettled
Based on learning
Not every group of brothers will end up with the best boss (allSettled method). He doesn’t care if brothers die or live. He just wants to see if they do it, and his job is to get all the results back.
The promise.allSettled () method takes an array as an argument, each member of which is a Promise object, and returns a new Promise object. The state of the returned Promise object will change only after all the Promise objects in the parameter array have state changes (whether this is a pity or Rejected).
Using the above example, let’s see how this differs from the promise.all method.
// 1
Promise.allSettled([p1, p2, p3])
.then((data) = > console.log(data)) / / [1, 2, 3]
.catch((error) = > console.log(error));
// 2. There are failed promises
Promise.allSettled([p1, p2, p3, p4])
.then((data) = > console.log(data))
.catch((error) = > console.log(error)); // error
// 3. Multiple failed promises exist
Promise.allSettled([p1, p2, p3, p4, p5])
.then((data) = > console.log(data))
.catch((error) = > console.log(error)); // error
// 4. Pass the String type
Promise.allSettled("zc").then((data) = > console.log(data));
Copy the code
From the output results, we can find:
allSettled
Methods will only succeed, not fail- Return result Each member is an object with a fixed format
- if
promise
Success, object property valuestatus: fulfilled
.value
Record success value - If the promise fails, the object property value
status: rejected
.reason
Record the failure cause.
- if
allSettled
The method is also acceptableIterator
Type parameters
Thought analysis
The biggest differences between the allSettled method and the ALL method are two:
allSettled
Methods do not failallSettled
Method returns have a fixed format
We can adapt the ALL method around these two points.
The All method is settled by counting successes. The allSettled method doesn’t count successes and failures, so we need to count the total number of successes/failures.
In the process of adding up the total size, construct allSettled data format on a case-to-case basis: push in success format, push in failure format.
The source code to achieve
Because of the handwritten basis of the ALL method, the above is not a long-winded implementation.
Promise.allSettled = function (promises) {
const result = [];
let count = 0;
let totalCount = 0; // Fulfill the promise number
return new Promise((resolve, reject) = > {
try {
for (let p of promises) {
let i = count;
count++; // Promise totals + 1
Promise.resolve(p)
.then((res) = > {
totalCount++;
// Returns success format data on success
result[i] = {
status: "fulfilled".value: res,
};
// The execution is complete
if (count === totalCount) {
resolve(result);
}
})
.catch((error) = > {
totalCount++;
// Return failure format data on failure
result[i] = {
status: "rejected".reason: error,
};
// The execution is complete
if(count === totalCount) { resolve(result); }});if (count === 0) { resolve(result); }}}catch(error) { reject(error); }}); };Copy the code
Promise.race
Based on learning
The race approach is visually a race mechanism that recognizes only the first runner, whether successful or unsuccessful.
The promise.race () method again takes multiple Promise instances and wraps them into a new Promise instance.
const p = Promise.race([p1, p2, p3]);
Copy the code
In the above case, as long as one instance of P1, P2 and P3 changes the state first, the state of P will change accordingly. The return value of the first changed Promise instance is passed to p’s callback.
const p1 = new Promise((resolve, reject) = > {
setTimeout(() = > {
resolve(1)},1000)})const p2 = new Promise((resolve, reject) = > {
setTimeout(() = > {
reject(2)},2000)})const p3 = 3;
// Success first, failure later
Promise.race([p1, p2]).then(res= > {console.log(res)}) / / 1
// Synchronous first, asynchronous later
Promise.race([p1, p3]).then(res= > console.log(res)) / / 3
// String
Promise.race('zc').then(res= > console.log(res)) // z
Copy the code
Thought analysis
The Race method is less convoluted and returns a promise as soon as it changes state.
So we simply listen for each promise’s then and catch methods, and call resolve and Reject when state changes.
The source code to achieve
Promise.race(promises) {
return new Promise((resolve, reject) = > {
for (let p of promises) {
// promise. resolve converts p to prevent passing in non-Promise instances
// The race execution mechanism returns the result of a state change for that instance
// So listen
Promise.resolve(p).then(resolve).catch(reject); }})}Copy the code
After the language
I am battlefield small bag, a fast growing small front end, I hope to progress together with you.
If you like xiaobao, you can pay attention to me in nuggets, and you can also pay attention to my small public number – Xiaobao learning front end.
All the way to the future!!