preface
(ಥ﹏ಥ) the interviewer asked him to write a Promise.all about a real event that really happened to a friend. The friend didn't play well on the spot and didn't write it out. Afterwards, he asked the interviewer for a vague evaluation that the foundation was not solid enough and he didn't master the principle knowledge... Of course, this is not the only problem that failed the whole interview, There must be other reasons.
But it sounded an alarm for us: Promise handwritten implementation and Promise static method implementation have long been high-frequency questions in the interview. If you don't know much about them, it will delay you for 10 minutes. Let's work together until they understand O(∩∩) O
Common interview handwriting series
Fat head fish wants to do something recently. I hope to write the common handwritten questions in the front-end interview into a series, and try to explain the knowledge and principles involved. If you are also interested in this series, welcome to learn together. Oh, at present, 66 + handwritten questions have been realized!
Promise.resolve
Brief review
- Promise.resolve(value) method returns a value parsed with the given value Promise Object.
- If the value is a promise, the promise will be returned;
- If this value is thenable (i.e. with "then" Method), the returned promise will "follow" the thenable object and adopt its final state; Otherwise, the returned promise will be completed with this value.
This is the explanation on MDN. Let's take a look one by one
- Promise.resolve the final result is also a promise and is closely related to the value passed in by promise.resolve (this value)
- The passed in parameter can be a Promise instance, and the result of this function is to return the instance directly
- The most important thing here is to understand the following. It can be understood that the Promise final state is the value output by the thenable object
Small example
// 1. Non Promise object, non thenable object Promise.resolve(1).then(console.log) // 1 // 2. Promise object success status const p2 = new Promise((resolve) => resolve(2)) Promise.resolve(p2).then(console.log) // 2 // 3. Promise object failure status const p3 = new Promise((_, reject) => reject('err3')) Promise.resolve(p3).catch(console.error) // err3 // 4. thenable object const p4 = { then (resolve) { setTimeout(() => resolve(4), 1000) } } Promise.resolve(p4).then(console.log) // 4 // 5. Nothing passed Promise.resolve().then(console.log) // undefined
Source code implementation
Promise.myResolve = function (value) { // It is a Promise instance. You can return it directly if (value && typeof value === 'object' && (value instanceof Promise)) { return value } // Otherwise, all other cases will be packaged by Promise return new Promise((resolve) => { resolve(value) }) } // Test it, or use the example just now // 1. Non Promise object, non thenable object Promise.myResolve(1).then(console.log) // 1 // 2. Promise object success status const p2 = new Promise((resolve) => resolve(2)) Promise.myResolve(p2).then(console.log) // 2 // 3. Promise object failure status const p3 = new Promise((_, reject) => reject('err3')) Promise.myResolve(p3).catch(console.error) // err3 // 4. thenable object const p4 = { then (resolve) { setTimeout(() => resolve(4), 1000) } } Promise.myResolve(p4).then(console.log) // 4 // 5. Nothing passed Promise.myResolve().then(console.log) // undefined
doubt
From the source code implementation, I don't see the special treatment of thenable objects! In fact, it really doesn't need to be processed in Promise.resolve. The real processing place should be in the Promise constructor. If you are interested in this, you will write the Promise implementation article right away. Look forward to your reading.
Promise.reject
Brief review
The Promise.reject() method returns a Promise object with a rejection reason.
Promise.reject(new Error('fail')) .then(() => console.log('Resolved'), (err) => console.log('Rejected', err)) // Output the following // Rejected Error: fail // at <anonymous>:2:16
Source code implementation
The reject implementation is relatively simple, as long as a new Promise is returned and the result status is set to reject
Promise.myReject = function (value) { return new Promise((_, reject) => { reject(value) }) } // Test it Promise.myReject(new Error('fail')) .then(() => console.log('Resolved'), (err) => console.log('Rejected', err)) // Rejected Error: fail // at <anonymous>:9:18
Promise.all
Brief review
The Promise.all() method is used to wrap multiple Promise instances into a new Promise instance. This static method should be the most common in an interview
const p = Promise.all([p1, p2, p3])
The final state of p is determined by p1, p2 and p3, which is divided into two cases.
(1) Only when the states of p1, p2 and p3 become fully, the state of p will become fully. At this time, the return values of p1, p2 and p3 form an array and are passed to the callback function of p.
(2) As long as one of p1, p2 and p3 is rejected, the status of p becomes rejected. At this time, the return value of the first rejected instance will be passed to p's callback function.
const p1 = Promise.resolve(1) const p2 = new Promise((resolve) => { setTimeout(() => resolve(2), 1000) }) const p3 = new Promise((resolve) => { setTimeout(() => resolve(3), 3000) }) const p4 = Promise.reject('err4') const p5 = Promise.reject('err5') // 1. All promises succeeded const p11 = Promise.all([ p1, p2, p3 ]) .then(console.log) // [ 1, 2, 3 ] .catch(console.log) // 2. One Promise failed const p12 = Promise.all([ p1, p2, p4 ]) .then(console.log) .catch(console.log) // err4 // 3. Two promises failed. You can see that the final output is err4, and the return value of the first failure const p13 = Promise.all([ p1, p4, p5 ]) .then(console.log) .catch(console.log) // err4
Source code implementation
Promise.myAll = (promises) => { return new Promise((rs, rj) => { // Counter let count = 0 // Storage results let result = [] const len = promises.length if (len === 0) { return rs([]) } promises.forEach((p, i) => { // Note that some array items may not be Promise and need to be converted manually Promise.resolve(p).then((res) => { count += 1 // Collect the return value of each Promise result[ i ] = res // When all promises are successful, set the returned Promise result to result if (count === len) { rs(result) } // If one Promise catch in the listening array item fails, the Promise returned by us will also fail }).catch(rj) }) }) } // Test it const p1 = Promise.resolve(1) const p2 = new Promise((resolve) => { setTimeout(() => resolve(2), 1000) }) const p3 = new Promise((resolve) => { setTimeout(() => resolve(3), 3000) }) const p4 = Promise.reject('err4') const p5 = Promise.reject('err5') // 1. All promises succeeded const p11 = Promise.myAll([ p1, p2, p3 ]) .then(console.log) // [ 1, 2, 3 ] .catch(console.log) // 2. One Promise failed const p12 = Promise.myAll([ p1, p2, p4 ]) .then(console.log) .catch(console.log) // err4 // 3. Two promises failed. You can see that the final output is err4, and the return value of the first failure const p13 = Promise.myAll([ p1, p4, p5 ]) .then(console.log) .catch(console.log) // err4 // It is consistent with the return of native Promise.all
Promise.allSettled
Brief review
Sometimes, we want to wait until a group of asynchronous operations have ended, whether each operation is successful or failed, before proceeding to the next operation. Obviously, promise.all (as long as one fails, the result will enter the failed state) is not suitable, so Promise.allSettled is available
Promise.allSettled() method takes an array as a parameter. Each member of the array is a promise object and returns a new promise object. Only when the state of all promise objects in the parameter array changes (whether fully or rejected), the state of the returned promise object will change. Once the state changes, the state is always fully and will not become rejected
Let's take the above example as an example to see how it is different from Promise.all
const p1 = Promise.resolve(1) const p2 = new Promise((resolve) => { setTimeout(() => resolve(2), 1000) }) const p3 = new Promise((resolve) => { setTimeout(() => resolve(3), 3000) }) const p4 = Promise.reject('err4') const p5 = Promise.reject('err5') // 1. All promises succeeded const p11 = Promise.allSettled([ p1, p2, p3 ]) .then((res) => console.log(JSON.stringify(res, null, 2))) // output /* [ { "status": "fulfilled", "value": 1 }, { "status": "fulfilled", "value": 2 }, { "status": "fulfilled", "value": 3 } ] */ // 2. One Promise failed const p12 = Promise.allSettled([ p1, p2, p4 ]) .then((res) => console.log(JSON.stringify(res, null, 2))) // output /* [ { "status": "fulfilled", "value": 1 }, { "status": "fulfilled", "value": 2 }, { "status": "rejected", "reason": "err4" } ] */ // 3. Two promises failed const p13 = Promise.allSettled([ p1, p4, p5 ]) .then((res) => console.log(JSON.stringify(res, null, 2))) // output /* [ { "status": "fulfilled", "value": 1 }, { "status": "rejected", "reason": "err4" }, { "status": "rejected", "reason": "err5" } ] */
You can see:
- Whether it is all successful or partially failed, it will eventually enter the. then callback of Promise.allSettled
- In the final return value, both successful and failed items have a status attribute. The value of success is full and failure is rejected
- In the final return value, success contains the value attribute, while failure is the reason attribute
Source code implementation
Promise.myAllSettled = (promises) => { return new Promise((rs, rj) => { let count = 0 let result = [] const len = promises.length // If the array is empty, return empty data directly if (len === 0) { return resolve([]) } promises.forEach((p, i) => { Promise.resolve(p).then((res) => { count += 1 // Successful attribute setting result[ i ] = { status: 'fulfilled', value: res } if (count === len) { rs(result) } }).catch((err) => { count += 1 // Failed property settings result[i] = { status: 'rejected', reason: err } if (count === len) { rs(result) } }) }) }) } // Test it const p1 = Promise.resolve(1) const p2 = new Promise((resolve) => { setTimeout(() => resolve(2), 1000) }) const p3 = new Promise((resolve) => { setTimeout(() => resolve(3), 3000) }) const p4 = Promise.reject('err4') const p5 = Promise.reject('err5') // 1. All promises succeeded const p11 = Promise.myAllSettled([ p1, p2, p3 ]) .then((res) => console.log(JSON.stringify(res, null, 2))) // output /* [ { "status": "fulfilled", "value": 1 }, { "status": "fulfilled", "value": 2 }, { "status": "fulfilled", "value": 3 } ] */ // 2. One Promise failed const p12 = Promise.myAllSettled([ p1, p2, p4 ]) .then((res) => console.log(JSON.stringify(res, null, 2))) // output /* [ { "status": "fulfilled", "value": 1 }, { "status": "fulfilled", "value": 2 }, { "status": "rejected", "reason": "err4" } ] */ // 3. Two promises failed const p13 = Promise.myAllSettled([ p1, p4, p5 ]) .then((res) => console.log(JSON.stringify(res, null, 2))) // output /* [ { "status": "fulfilled", "value": 1 }, { "status": "rejected", "reason": "err4" }, { "status": "rejected", "reason": "err5" } ] */
Promise.race
Brief review
The Promise.race() method also packages multiple Promise instances into a new Promise instance.
const p = Promise.race([p1, p2, p3])
As long as one of p1, p2 and p3 takes the lead in changing the state, the state of p will change accordingly. The return value of the Promise instance that changed first is passed to the callback function of p.
const p1 = new Promise((resolve, reject) => { setTimeout(resolve, 500, 1) }) const p2 = new Promise((resolve, reject) => { setTimeout(resolve, 100, 2) }) Promise.race([p1, p2]).then((value) => { console.log(value) // 2 }) Promise.race([p1, p2, 3]).then((value) => { console.log(value) // 3 })
Source code implementation
Smart, you must know how to implement it immediately. As long as you know which instance changes first, Promise.race will follow the result, and you can write the following code
Promise.myRace = (promises) => { return new Promise((rs, rj) => { promises.forEach((p) => { // Wrap p once to prevent non Promise objects // And align to listen, and pass the resolve and reject of the Promise we returned to p. which changes the state first, the Promise we returned will also be in what state Promise.resolve(p).then(rs).catch(rj) }) }) } // Test it const p1 = new Promise((resolve, reject) => { setTimeout(resolve, 500, 1) }) const p2 = new Promise((resolve, reject) => { setTimeout(resolve, 100, 2) }) Promise.myRace([p1, p2]).then((value) => { console.log(value) // 2 }) Promise.myRace([p1, p2, 3]).then((value) => { console.log(value) // 3 })
ending
Maybe you and I have never met, but it's probably too late to meet. hope here Can become your habitat, I am willing to harvest joy with you and go to grow.
The above is the first analysis of handwriting implementation principle! You are welcome to correct the possible errors and problems