Review the usage
Let promise = new promise ((resolve, reject) => {resolve('reject')}) promise. reason => {})Copy the code
Promise
It’s a class and when you execute that class, you have to pass in an executor and the executor executes immediatelyPromise
There are three states: successfulfilled
, failed,rejected
And wait forpending
.
-
Once the state is determined, it cannot be changed
-
pending -> fulfilled
-
pending -> rejected
- The resolve and Rejected functions are used to change the state
- resolve : fulfilled
- reject : rejected
-
What the then method does internally is determine the state and if the state is a successful call to a successful callback and if the state is a failure call to a failed callback the then method is defined in the prototype object
-
Then a successful callback takes one parameter to indicate the value after success. Then a failed callback takes one parameter to indicate the reason for failure
The core logic
Defining state enumeration
- Define three states as enumerations. To make it easier for future non-string inputs to cause errors
The following code
const PENDING = "pending";
const RESOLVED = "resolved";
const REJECTED = "rejected";
Copy the code
Define a class
- As you know from reviewing usage, the new Promise is passed in during the procedure
The following code
class MyPromise { constructor(executor) { executor(this.resolve, this.reject); }}Copy the code
State changes
- The status can be changed only once. , to make the judgment of non-wait state
- Modify the corresponding status
The following code
class MyPromise { constructor(executor) { executor(this.resolve, this.reject); } status = PENDING; Resolve = () => {// The status can be changed only once. if (this.status ! == PENDING) return; This. Status = RESOLVED; }; reject = () => { if (this.status ! == PENDING) return; // This. Status = REJECTED; }; }Copy the code
then
- There will be successful and unsuccessful callbacks
- Callbacks have a reason, so you need to save the reason when you change the state and return it when you call back
The following code
class MyPromise { constructor(executor) { executor(this.resolve, this.reject); } status = PENDING; value = undefined reason = undefined resolve = value => { if (this.status ! == PENDING) return; this.status = RESOLVED; This. value = value}; reject = reason => { if (this.status ! == PENDING) return; this.status = REJECTED; // this. Reason = reason}; then(successCallback, FailCallback (this.status === RESOLVED) {successCallback(this.value)} else if(this.status === failCallback) {// 2 Determine the status if(this.status === RESOLVED) {successCallback(this.value)} else if(this.status === REJECTED){ failCallback(this.reason) } } }Copy the code
Asynchronous logic
Eg: Asynchronous callback.
Let promise = new promise ((resolve, reject) => {setTimeout(() => {resolve(" succeed ")); Reject (' reject ')}, 20000)}); promise.then( (value) => { console.log("----value", value); }, (reason) => { console.log("----reason", reason); });Copy the code
In this case, successCallback or failCallback will not be executed.
Modification:
then
To determine whether or notWait state
If so, save the successful and failed callback- in
resolve
、reject
To determine if the callback exists, it does, and takes the corresponding argument back to the call.
The following code
class MyPromise { constructor(executor) { executor(this.resolve, this.reject); } status = PENDING; value = undefined; reason = undefined; SuccessCallback = undefined; failCallback = undefined; resolve = (value) => { if (this.status ! == PENDING) return; this.status = RESOLVED; this.value = value; This.successcallback && this.successCallback(this.value)}; reject = (reason) => { if (this.status ! == PENDING) return; this.status = REJECTED; this.reason = reason; FailCallback && this.failCallback(this.reason)}; then(successCallback, failCallback) { if (this.status === RESOLVED) { successCallback(this.value); } else if (this.status === REJECTED) { failCallback(this.reason); } else {// 3 Save callback this. SuccessCallback = successCallback; this.failCallback = failCallback; }}}Copy the code
The then method makes multiple calls to add multiple handlers
The then methods under the same Promise object can be called multiple times.
eg:
Let promise = new MyPromise((resolve, reject) => {setTimeout(() => {resolve(" succeed ")); Reject (" failure "); }, 2000); // resolve(" resolve "); // reject(" reject "); }); promise.then( (value) => { console.log("----value", value); }, (reason) => { console.log("----reason", reason); }); promise.then((value) => { console.log(1, value); }); promise.then((value) => { console.log(2, value); }); promise.then((value) => { console.log(3, value); });Copy the code
At this point, the current code will only save the last callback, so we need
- will
callback
Transform to an array
The following code
class MyPromise { constructor(executor) { executor(this.resolve, this.reject); } status = PENDING; value = undefined; reason = undefined; // 1 becomes array successCallback = []; failCallback = [] resolve = (value) => { if (this.status ! == PENDING) return; this.status = RESOLVED; this.value = value; // 2 while the length is determined, shift keeps pushing the first element, The this value spread in the while (this) successCallback) length) enclosing successCallback. The shift () (value) this.}; reject = (reason) => { if (this.status ! == PENDING) return; this.status = REJECTED; this.reason = reason; // 2 while the length is determined, shift keeps pushing the first element, While (this.failcallback.length) this.failcallback.shift ()(this.reason)}; then(successCallback, failCallback) { if (this.status === RESOLVED) { successCallback(this.value); } else if (this.status === REJECTED) { failCallback(this.reason); } else {/ / 3 to push this. SuccessCallback. Push (successCallback); this.failCallback.push(failCallback); }}}Copy the code
Chain calls
The then method can be called chained. The callback of the later THEN method returns the value of the callback of the previous THEN method
Common values
Let promise = new promise ((resolve, reject) => {resolve(" succeed "); }); promise.then((value) => { console.log(1, value); return 100 }).then(value => { console.log(value); })Copy the code
The output is as follows
So we need
-
Return a new promise
-
What successCallback returns also needs to be brought to the next.then
-
“Then” requires immediate execution. -> Put it into a new Promise, executed immediately
The following code
Then (successCallback, failCallback) {// define a new promise. let promise2 = new MyPromise((resolve, Reject) => {if (this.status === RESOLVED) {let x = successCallback(this.value); resolve(x) } else if (this.status === REJECTED) { failCallback(this.reason); } else { this.successCallback.push(successCallback); this.failCallback.push(failCallback); }}); // Return the new Promise to complete the chain call return promise2; }Copy the code
The callback returns another Promise
If another Promise is returned, we need to determine whether the state of the Promise is successful or failed, calling resolve if it is successful and reject if it is failed.
The logic that needs to be handled is as follows
-
Determine whether the value of x is normal or a promise object
-
If the value is normal, call resolve directly
-
If it is a Promise object, look at the results returned by the Promsie object
-
The result returned by the Promise object determines whether to call resolve or reject
We define a new method, resolvePromise (not a method in the class, but defined outside the class), to make it easier to call uniformly. The arguments passed are the values of callback, resolve, reject
Function resolvePromise (x, reject, reject) {if (x instanceof MyPromise) {if (x instanceof MyPromise) { reject); } else {resolve(x); }}Copy the code
Then is modified as follows
then(successCallback, failCallback) { let promise2 = new MyPromise((resolve, reject) => { if (this.status === RESOLVED) { let x = successCallback(this.value); ResolvePromise (x, resolve, reject) } else if (this.status === REJECTED) { failCallback(this.reason); } else { this.successCallback.push(successCallback); this.failCallback.push(failCallback); }}); return promise2; }Copy the code
Special loop call
In special cases, you can make the return promise itself, which results in a circular invocation.
eg:
Let promise = new promise ((resolve, reject) => {resolve(" succeed "); }); let promise2 = promise .then((value) => { console.log(1, value); return promise2; })Copy the code
The results are as follows
Therefore, we need to determine whether the content of return is itself.
Modify the following
class MyPromise { .... then(successCallback, failCallback) { let promise2 = new MyPromise((resolve, reject) => { if (this.status === RESOLVED) { let x = successCallback(this.value); // pass the promise2 itself to resolvePromise(promise2, x, resolve, reject) } else if (this.status === REJECTED) { failCallback(this.reason); } else { this.successCallback.push(successCallback); this.failCallback.push(failCallback); }}); return promise2; }} function resolvePromise (promise2, x, resolve, reject) { if(promise2 === x) { return reject(new TypeError('Chaining cycle detected for promise #<Promise>')) } if (x instanceof MyPromise) { x.then(resolve, reject); } else { resolve(x); }}Copy the code
However, there is a new problem. When the resolvePromise is passed, the promise has not yet been received. What do we need to do
- Process to async
So we just add settime.
Class MyPromise {... then(successCallback, failCallback) { let promise2 = new MyPromise((resolve, Reject) => {if (this.status === RESOLVED) { SetTimeout (() => {let x = successCallback(this.value); resolvePromise(promise2, x, resolve, reject); }, 0); } else if (this.status === REJECTED) { failCallback(this.reason); } else { this.successCallback.push(successCallback); this.failCallback.push(failCallback); }}); return promise2; }}Copy the code
Capture the error
So far, we are dealing with normal cases where we need to add error compatibility to ensure that the code executes properly.
- Add the try catch
The following code
Constructor (executor) {constructor(executor) {// Add try catch try {executor(this.resolve, this.reject); } catch (error) {// reject(error)}}.... then(successCallback, failCallback) { let promise2 = new MyPromise((resolve, Reject) => {if (this.status === RESOLVED) {setTimeout(() => {// add try catch try {let x = successCallback(this.value); resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error) } }, 0); } else if (this.status === REJECTED) { failCallback(this.reason); } else { this.successCallback.push(successCallback); this.failCallback.push(failCallback); }}); return promise2; }}Copy the code
Supplement processing of other states
For now, we’re just dealing with the resolve state, and we’re continuing to add code for the Reject state
class MyPromise { ... then(successCallback, failCallback) { let promise2 = new MyPromise((resolve, reject) => { if (this.status === RESOLVED) { setTimeout(() => { try { let x = successCallback(this.value); resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error) } }, 0); } else if (this.status === REJECTED) {setTimeout(() => {let x = failCallback(this.reason)} resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error) } }, 0); } else { this.successCallback.push(successCallback); this.failCallback.push(failCallback); }}); return promise2; }}Copy the code
Asynchronous processing
In combination with the previous successes and failures, we also need to deal with asynchronous callback functions
class MyPromise { constructor(executor) { try { executor(this.resolve, this.reject); } catch (error) { this.reject(error) } } status = PENDING; value = undefined; reason = undefined; successCallback = []; failCallback = []; resolve = (value) => { if (this.status ! == PENDING) return; this.status = RESOLVED; this.value = value; / / / / is no longer required value while (this) successCallback) length) enclosing successCallback. The shift () (enclosing value); while (this.successCallback.length) this.successCallback.shift()(); }; reject = (reason) => { if (this.status ! == PENDING) return; this.status = REJECTED; this.reason = reason; // While (this.failcallback.length) this.failcallback.shift ()(this.reason); while (this.failCallback.length) this.failCallback.shift()(); }; then(successCallback, failCallback) { let promise2 = new MyPromise((resolve, reject) => { if (this.status === RESOLVED) { setTimeout(() => { try { let x = successCallback(this.value); resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error) } }, 0); } else if (this.status === REJECTED) { setTimeout(() => { try { let x = failCallback(this.reason) resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error) } }, 0); } else {this. SuccessCallback. Push (() = > {/ / put the success function to copy a setTimeout (() = > {try {let x = successCallback(this.value); resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error) } }, 0); }); SetTimeout (() => {try {let x = failCallback(this.reason)) resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error) } }, 0); }); }}); return promise2; }}Copy the code
Make the arguments of the then method optional
Let’s start with a piece of code
Let promise = new promise ((resolve, reject) => {resolve(" succeed "); // reject(" reject "); }); promise .then() .then() .then((value) => { console.log(value); });Copy the code
In then, we can choose not to pass parameters, and get the parameters passed down layer by layer in the last place where there are parameters.
In effect, the code above is equivalent to the code below
promise
.then(value => value)
.then(value => value)
.then((value) => {
console.log(value);
});
Copy the code
so
-
Check whether there is a callback
class MyPromise {
.
Then (successCallback, failCallback) {/ / add the optional parameters successCallback = successCallback | | (value = > value); / / failure callback to throw failCallback = failCallback | | (” reason = > {throw reason});
let promise2 = new MyPromise((resolve, reject) => { ... } return promise2; Copy the code
}}
A static method
all
Promises allow us to execute multiple promises and return them all at once. This is the all method. Note that this is a static method. The following code is an example. In particular, a reject is returned, and none of the others is returned
Let p1 = new Promise((resolve, reject) => {setTimeout(() => {resolve(" p1")); }, 2000)}); Let p2 = new Promise((resolve, reject) => {resolve(" succeed P2 "); }); let p = Promise.all(['a1', 'a2', p1, p2, 'c1']) p.then(res => console.log(res))Copy the code
As you can see from the above code,
all
I’m passing an array- The pass element can be a non-Promise instance
The implementation code
class MyPromise { ... All static all(arr) {let result = []; // Let index = 0; Return new MyPromise((resolve, reject) => {function addData(key, value) {result[key] = value; // each time +1 index++; If (index === arr.length) {resolve(result); } } for (let idx = 0; idx < arr.length; idx++) { const current = arr[idx]; If (current instanceof MyPromise) {if (current instanceof MyPromise) { value), (reason) => reject(reason) ); } else {// If you don't add the result array addData(idx, current); }}}); }}Copy the code
resolve
Resolve converts the passed argument into a promise. Make subsequent chained calls as well. Examples are as follows
Function p1() {return new Promise((resolve, reject) => {setTimeout(() => {resolve(" p1"); }, 2000); }); } Promise.resolve(10).then((res) => console.log(res)); Promise.resolve(p1()).then((res) => console.log(res));Copy the code
The log is as follows
It’s really simple.
-
Determine if it is an instance of Promise, if it is returned directly
-
Resolve out
class MyPromise {
. Resolve static resolve (value) {// If (value instanceof MyPromise) return value; Return MyPromise(resolve => resolve(value)); }}
finally
We know that finally can be called anyway. So let’s review and see how the example works, okay
let p1 = new Promise((resolve, reject) => { console.log('..... '); P1 resolve (" success "); }); p1.then((res) => console.log(res)).finally(() => { console.log("----1"); }); p1.finally(() => { console.log("----2"); }).then((res) => console.log(res))Copy the code
The log is as follows
We can see that
-
Where can finally be executed in order
-
Finally is followed by then to get the value returned
-
It is important to note that there is the concept of microtasks and macro tasks. Then followed by finally or finally followed by then is the second microtask.
-
Consider the case where finally comes first and the value returned is asynchronous.
The following code
class MyPromise { ... Finally (callback) {// Since it can be called chained, we need to return a promise and call the. Then method directly. Return myPromise.resolve (callback()). Then (() => value) }, reason => { return MyPromise.resolve(callback()).then(() => { throw reason }) }) } }Copy the code
catch
Without further ado, let’s look at examples
Let p1 = new Promise((resolve, reject) => {reject(' reject ')}); p1.then((res) => console.log(res)) .catch(err => { console.log(err); })Copy the code
The log is as follows;
We can see that
-
Then can be caught in a catch when it does not pass a failure callback.
The implementation is simple, just put callback into THEN (undefind, callback)
The code implementation is as follows
class MyPromise {
...
catch (failCallback) {
return this.then(undefined, failCallback)
}
}
Copy the code
The end of the
At this point, all logic is complete. Here is the code overview
const PENDING = 'pending'; // this is a big pity; // const REJECTED = 'REJECTED '; Constructor (executor) {constructor (executor) {try {executor(this.resolve,); this.reject) } catch (e) { this.reject(e); } // promsie status = PENDING; // Value = undefined; // The reason for the failure = undefined; // Callback successCallback = []; // failCallback = []; Resolve = value => {// If the status is not waiting to prevent the program from executing down if (this.status! == PENDING) return; // This. Status = depressing; // This. Value = value; / / determine whether success callback If there is a call while (this) successCallback) length) enclosing successCallback. Shift () ()} reject = "reason = > {/ / If the state is not waiting to prevent the program from executing down if (this.status! == PENDING) return; // This. Status = REJECTED; // This. Reason = reason; While (this.failcallback.length) this.failcallback.shift ()()} then (successCallback, SuccessCallback failCallback) {/ / add the optional parameter = successCallback | | (value = > value); / / failure callback to throw failCallback = failCallback | | (" reason = > {throw reason}); let promsie2 = new MyPromise((resolve, (this. Status === depressing) {setTimeout(() => {// add fault-proof try {let x == successCallback(this.value); resolvePromise(promsie2, x, resolve, reject) }catch (e) { reject(e); }}, 0)}else if (this.status === REJECTED) {setTimeout(() => {let x = failCallback(this.reason); resolvePromise(promsie2, x, resolve, reject) }catch (e) { reject(e); }}, 0)} else {/ / waiting / / success callback callbacks and failure to store up enclosing successCallback. Push ((() = > {setTimeout () = > {try {let x = successCallback(this.value); resolvePromise(promsie2, x, resolve, reject) }catch (e) { reject(e); }}}, 0)); this.failCallback.push(() => { setTimeout(() => { try { let x = failCallback(this.reason); resolvePromise(promsie2, x, resolve, reject) }catch (e) { reject(e); }}}, 0)); }}); return promsie2; } finally(callback) {// Since it can be called chained, we need to return a promise and call the. Then method directly. Return myPromise.resolve (callback()). Then (() => value) }, reason => { return MyPromise.resolve(callback()).then(() => { throw reason }) }) } catch (failCallback) { return This. Then (undefined, failCallback)} // define static method all static all(arr) {let result = []; // Let index = 0; Return new MyPromise((resolve, reject) => {function addData(key, value) {result[key] = value; // each time +1 index++; If (index === arr.length) {resolve(result); } } for (let idx = 0; idx < arr.length; idx++) { const current = arr[idx]; If (current instanceof MyPromise) {if (current instanceof MyPromise) { value), (reason) => reject(reason) ); } else {// If you don't add the result array addData(idx, current); }}}); Resolve static resolve (value) {// If (value instanceof MyPromise) return value; Return MyPromise(resolve => resolve(value)); If x is a normal value, call resolve. If x is a promise object, call resolve. If x is a promise object, call resolve Reject function resolvePromise (promsie2, x, resolve, reject) { if (promsie2 === x) { return reject(new TypeError('Chaining cycle detected for promise #<Promise>')) } if (x Instanceof MyPromise) {// createPromise (value => resolve(value), reason => reject(reason)); x.then(resolve, reject); } else {resolve(x); }}Copy the code