Review the usage

Let promise = new promise ((resolve, reject) => {resolve('reject')}) promise. reason => {})Copy the code
  1. PromiseIt’s a class and when you execute that class, you have to pass in an executor and the executor executes immediately
  2. PromiseThere are three states: successfulfilled, failed,rejectedAnd wait forpending.
  • Once the state is determined, it cannot be changed

  • pending -> fulfilled  

  • pending -> rejected  

  1. The resolve and Rejected functions are used to change the state
  • resolve : fulfilled  
  • reject : rejected  
  1. 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

  2. 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:

  • thenTo determine whether or notWait stateIf so, save the successful and failed callback
  • inresolverejectTo 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

  • willcallbackTransform 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,

  • allI’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