At the forefront of

As the saying goes, only by standing on the shoulders of the previous giants can you see further. After reading the promise written by the giants, the meridians and collaterals of the whole body were unblocked, such as being pointed at the Baihui point with golden fingers by our ancestors. Since then, the operation of the size of the Sunday a few points more skilled than usual. However, shoulder Well point, Yongquan point, everyday Haishi, will dull pain. As I pondered over and over, I was probably unable to fully comprehend the subtlety of the previous great master’s article. Solid painstakingly feeling, square get this article. Dare not compare with predecessors, only as a partial complement

Straight guys don’t know how to lay, so they just do it

Here we use ES6 to implement, so that the structure is more obvious, if you are not familiar with ES6 friends, please read Ruan Yifeng teacher ECMAScript 6 introduction

class myPromise {
            constructor(executor){
                this.value = null;
                this.reason = null;
                const resolve = value= >{
 this.value = value;  }  const reject = reason= > {  this.reason = reason;  }  try {  executor(resolve,reject)  } catch (e) {  reject(e)  }  }  then(onFulfilled,onRejected){  onFulfilled(this.value)  onRejected(this.reason)  }  } Copy the code

Code parsing:

  1. MyPromise is a constructor that takes a higher-order function and accepts two inner functions, resolve and reject
  2. Any instance of myPromise can call the THEN method, which returns resolve as the first argument and Reject as the second

The first step in learning kung fu is (Since the palace) compared

Hands type

After observation, we found that the results of myPormise and Promise running we implemented were consistent (good guy!). There is only one small problem. We call P1’s THEN first, but P1’s THEN returns after P2’s THEN returns. We reserve doubt, continue to look!!

Resolve /reject singularity

This means that once resolve or reject is implemented in a promise, then resolve or reject is not implemented


And when YOU look at it, it’s not true

  1. The other promise returned only ‘p1-resolve’. Ours returned ‘p2-resolve2’ and ‘p2-reject1’.
  2. Other promise is not the same as our promise, so we still need a ‘p2-resolve1’. Resolve (‘p2-resolve1’); resolve(‘p2-resolve2’); resolve(‘p2-resolve2’); resolve(‘p2-resolve2’) This. value is reassigned by the latter. When the then function is executed, the ‘p2-resolve1’ automatically disappears.
  3. How do I fix it? The initial state is set as ‘pending’, and it has only one chance to change its state. It can become pending-> depressing or pending-> Rejected. Once the state is changed, it will not change again. (Doesn’t it look like pokemon evolution).
  4. In addition, to ensure the uniqueness of resolve/ Reject execution, resolve/ Reject is not executed when the state is not pending

So… (It’s you, Pikachu!!)

class myPromise {
            constructor(executor){
+ this.state = 'pending'; // Internal states are pending,resolve,reject
                this.value = null;
                this.reason = null;
 const resolve = value => { + if(this.state === 'pending'){ + this.state = 'fulfilled';  this.value = value; +}  }  const reject = reason => { + if(this.state === 'pending'){ + this.state = 'rejected';  this.reason = reason; +}  }  try {  executor(resolve,reject)  } catch (e) {  reject(e)  }  }  then(onFulfilled,onRejected){ + if(this.state === 'fulfilled'){  onFulfilled(this.value) +} + if(this.state === 'rejected') {  onRejected(this.reason) +}  }  } Copy the code

After modification and observation, the promise of our family became normal again.

Asynchronous execution of promises

Let’s clean up our test code a little to make it look fresh and natural

After cleaning up. Let’s add asynchrony

Upon observation, we found that our promise didn’t have any print

  1. If resolve is not yet executed, then is already executed, and the state is ‘pending’, so there is no execution in then
  2. How do we deal with this situation? The first thing to think about is that when resolve/reject is executed then the state is ‘pending’, and when resolve is executed without knowing the end. So we can only temporarily save all the parameters of the THEN (onFulfilled/onRejected), and wait for the corresponding method (onFulfilled/onRejected) to be implemented after the state changes later.
  3. When the state changes, the corresponding method of the staging is executed immediately

Based on the above, we modify our promise

class myPromise {
            constructor(executor){
this.state = 'pending'; // Internal states are pending,resolve,reject                this.value = null;
                this.reason = null;
+ this.onFulfilledCallbacks = []; + this.onRejectedCallbacks = [];  const resolve = value =>{  if(this.state === 'pending'){  this.state = 'fulfilled';  this.value = value; + this.onFulfilledCallbacks.forEach(fn=>fn(value))  }  }  const reject = reason => {  if(this.state === 'pending'){  this.state = 'rejected';  this.reason = reason; + this.onRejectedCallbacks.forEach(fn=>fn(reason))  }  }  try {  executor(resolve,reject)  } catch (e) {  reject(e)  }  }  then(onFulfilled,onRejected){  if(this.state === 'fulfilled'){  onFulfilled(this.value)  }  if(this.state === 'rejected') {  onRejected(this.reason)  } + if(this.state === 'pending') { + this.onFulfilledCallbacks.push(onFulfilled) + this.onRejectedCallbacks.push(onRejected) +}  }  } Copy the code

After that, let’s look at the resultsUpon observation, we found that our promise would normally display then callbacks, and there werebonusP1. then and p2.then are printed in the same order as when they were called.

Synchronous asynchronous consistency

Whether resolve/reject is synchronous or asynchronous in a promise, we want the execution returned by then to behave consistently. We create an execution comparison

After observation, we can find that

  1. A promise from someone else’s house, whether resolve is synchronous or asynchronous, then returns asynchronous
  2. If resolve is asynchronous, then returns asynchronous, and if resolve is synchronous, then returns synchronous

So how do you change it? Our goal is to keep the execution of THEN consistent, so we can only change all THEN to be asynchronous, for the reason explained in the previous section let’s modify our home promise

class myPromise {
            constructor(executor) {
this.state = 'pending'; // Internal states are pending,resolve,reject                this.value = null;
                this.reason = null;
 this.onFulfilledCallbacks = [];  this.onRejectedCallbacks = [];  const resolve = value => {  if (this.state === 'pending') {  this.state = 'fulfilled';  this.value = value;  this.onFulfilledCallbacks.forEach(fn => fn(value))  }  }  const reject = reason => {  if (this.state === 'pending') {  this.state = 'rejected';  this.reason = reason;  this.onRejectedCallbacks.forEach(fn => fn(reason))  }  }  try {  executor(resolve, reject)  } catch (e) {  reject(e)  }  }  then(onFulfilled, onRejected) {  if (this.state === 'fulfilled') { + setTimeout(()=>{  onFulfilled(this.value) +})  }  if (this.state === 'rejected') { + setTimeout(()=>{  onRejected(this.reason) +})  }  if (this.state === 'pending') { + this.onFulfilledCallbacks.push((value)=>setTimeout(()=>{onFulfilled(value)})) + this.onRejectedCallbacks.push((reason)=>setTimeout(()=>{onRejected(reason)}))  }  }  } Copy the code

Then we’ll look at execution

Now our promise is more like someone else’s

Why arrays

Careful friend will find this. OnFulfilledCallbacks and enclosing onRejectedCallbacks is an array, in then method, when the state = = = ‘pending’, We add onFulfilled and onRejected array into the array, instead of directly assigned to this. OnFulfilledCallbacks/enclosing onRejectedCallbacks. You might think there’s nothing wrong with assigning directly.

Let’s modify our promise to store it directly in variables

class myPromise {
        constructor(executor) {
this.state = "pending"; // Internal states are pending,resolve,reject          this.value = null;
          this.reason = null;
 this.onFulfilledCallbacks = [];  this.onRejectedCallbacks = [];  const resolve = (value) => {  if (this.state === "pending") {  this.state = "fulfilled";  this.value = value; - this.onFulfilledCallbacks.forEach((fn) => fn(value)); + this.onFulfilledCallbacks(value);  }  };  const reject = (reason) => {  if (this.state === "pending") {  this.state = "rejected";  this.reason = reason;  this.onRejectedCallbacks.forEach((fn) => fn(reason));  }  };  try {  executor(resolve, reject);  } catch (e) {  reject(e);  }  }  then(onFulfilled, onRejected) {  if (this.state === "fulfilled") {  setTimeout(() => {  onFulfilled(this.value)  })  }  if (this.state === "rejected") {  setTimeout(() => {  onRejected(this.reason)  })  }  if (this.state === "pending") { - this.onFulfilledCallbacks.push((value)=>setTimeout(()=>{onFulfilled(value)})); + this.onFulfilledCallbacks = (value)=>setTimeout(()=>{onFulfilled(value)});  this.onRejectedCallbacks.push(onRejected);  }  }  } Copy the code

After observation, we found that:

  1. This is a big pity. The promise of others is fulfilled normally, and the onFulfilled two THEN callbacks are normally fulfilled, while the onFulfilled one of our family is only fulfilled the second THEN callback
  2. The problem with our promise is that when the second THEN executes, an onledCallbacks assignment overrides the first THEN. So here only the ondepressing of the second THEN callback will be performed

Let’s roll back the code to the previous section and move on to the next section

Chain calls

The THEN method returns a Promise instance at all times

Let’s take a closer look

  1. A promise that executes the first THEN method succeeds in executing the second THEN method
  2. The promise in our family is that after executing the first THEN method, the second THEN method returns an error
  3. Because our then method of promise doesn’t return a promise object

Let’s revise our promise according to our analysis

class myPromise {
        constructor(executor) {
this.state = "pending"; // Internal states are pending,resolve,reject          this.value = null;
          this.reason = null;
 this.onFulfilledCallbacks = [];  this.onRejectedCallbacks = [];  const resolve = (value) => {  if (this.state === "pending") {  this.state = "fulfilled";  this.value = value;  this.onFulfilledCallbacks.forEach((fn) => fn(value));  }  };  const reject = (reason) => {  if (this.state === "pending") {  this.state = "rejected";  this.reason = reason;  this.onRejectedCallbacks.forEach((fn) => fn(reason));  }  };  try {  executor(resolve, reject);  } catch (e) {  reject(e);  }  }  then(onFulfilled, onRejected) {  if (this.state === "fulfilled") {  setTimeout(() => {  onFulfilled(this.value)  })  }  if (this.state === "rejected") {  setTimeout(() => {  onRejected(this.reason)  })  }  if (this.state === "pending") {  this.onFulfilledCallbacks.push((value)=>setTimeout(()=>{onFulfilled(value)}));  this.onFulfilledCallbacks = (value)=>setTimeout(()=>{onFulfilled(value)});  } + return new myPromise((resolve,reject)=>{ + resolve() +})  }  } Copy the code

Once the modification is complete, let’s execute it

By observation we can see that

  1. P2-resolve-then2 has been successfully printed, but not in the right place for someone else’s promise

The return value problem for the second THEN

The parameter to the ondepressing of the second THEN is the return value of the first onfuifle

So let’s modify our execution code


Here’s a little tip that uses the union logical operator, console.log(). This function returns undefined, so it’s not

Two flowers, one on each table,

We observed that

  1. The second THEN of someone else’s promise will normally receive the value passed from the first THEN
  2. Promis does not. It is not surprising that the resolve method of the promise returned by then has no parameters!!

Let’s change our family promise along these lines

class myPromise {
            constructor(executor) {
this.state = "pending"; // Internal states are pending,resolve,reject                this.value = null;
                this.reason = null;
 this.onFulfilledCallbacks = [];  this.onRejectedCallbacks = [];  const resolve = (value) => {  if (this.state === "pending") {  this.state = "fulfilled";  this.value = value;  this.onFulfilledCallbacks.forEach((fn) => fn(value));  }  };  const reject = (reason) => {  if (this.state === "pending") {  this.state = "rejected";  this.reason = reason;  this.onRejectedCallbacks.forEach((fn) => fn(reason));  }  };  try {  executor(resolve, reject);  } catch (e) {  reject(e);  }  }  then(onFulfilled, onRejected) {  const promise2 = new myPromise((resolve, reject) => {  if (this.state === "fulfilled") {  setTimeout(() => { + // In case the ondepressing method fails, we will use trycatch to catch it + try { + let x = onFulfilled(this.value); + resolve(x) + } catch (error) { + reject(error) +}  })  }  if (this.state === "rejected") {  setTimeout(() => { + try { + let x = onRejected(this.reason); + // There's a little bit of a problem here, but let's do it this way, because it's more logical + reject(x) + } catch (error) { + reject(error) +}  })  }  if (this.state === "pending") {  this.onFulfilledCallbacks.push(value => { + try { + let x = onFulfilled(value) + resolve(x) + } catch (error) { + reject(error) +}  });  this.onRejectedCallbacks.push(reason => { + try { + let x = onRejected(reason) + // There's a little bit of a problem here, but let's do it this way, because it's more logical + reject(x) + } catch (error) { + reject(error) +}  });  }  })  return promise2;  }  } Copy the code

Finally, let’s look at the results

Observation pays off, and our promises are starting to look more and more like everyone else’s.

The return of the THEN function onFulfilled will be accepted by the onFulfilled method of the next THEN function

Let’s look at an example

A closer look reveals that

  1. P1-reject -then1(onRejected of the first THEN)=> P1-resolve -then2(onFulfilled of the second THEN)
  2. P2-reject -then1 => P2-reject -then2 => P2-reject -then2 => P2-reject -then2 => P2-reject -then2
  3. The difference here is that the promise reject doesn’t need to be passed.

I drew a picture


So let’s revise our family promise

class myPromise {
            constructor(executor) {
this.state = "pending"; // Internal states are pending,resolve,reject                this.value = null;
                this.reason = null;
 this.onFulfilledCallbacks = [];  this.onRejectedCallbacks = [];  const resolve = (value) => {  if (this.state === "pending") {  this.state = "fulfilled";  this.value = value;  this.onFulfilledCallbacks.forEach((fn) => fn(value));  }  };  const reject = (reason) => {  if (this.state === "pending") {  this.state = "rejected";  this.reason = reason;  this.onRejectedCallbacks.forEach((fn) => fn(reason));  }  };  try {  executor(resolve, reject);  } catch (e) {  reject(e);  }  }  then(onFulfilled, onRejected) {  const promise2 = new myPromise((resolve, reject) => {  if (this.state === "fulfilled") {  setTimeout(() => { // If ondepressing method fails, we will use trycatch to catch it try {  let x = onFulfilled(this.value);  resolve(x)  } catch (error) {  reject(error)  }  })  }  if (this.state === "rejected") {  setTimeout(() => {  try {  let x = onRejected(this.reason); - // There is a problem here for the moment. Let's write it this way because it is more logical - reject(x) + resolve(x)  } catch (error) {  reject(error)  }  })  }  if (this.state === "pending") {  this.onFulfilledCallbacks.push(value => {  try {  let x = onFulfilled(value)  resolve(x)  } catch (error) {  reject(error)  }  });  this.onRejectedCallbacks.push(reason => {  try {  let x = onRejected(reason) - // There is a problem here for the moment. Let's write it this way because it is more logical - reject(x) + reslove(x)  } catch (error) {  reject(error)  }  });  }  })  return promise2;  }  } Copy the code

That’s it. Let’s look at the results


bingo!!! Our promise is like other people’s promise

Adjust the structure

Now that the general structure is complete, we need to adjust it for the following comparison

class myPromise {
            constructor(executor) {
this.state = "pending"; // Internal states are pending,resolve,reject                this.value = null;
                this.reason = null;
 this.onFulfilledCallbacks = [];  this.onRejectedCallbacks = [];  const resolve = (value) => {  if (this.state === "pending") {  this.state = "fulfilled";  this.value = value;  this.onFulfilledCallbacks.forEach((fn) => fn(value));  }  };  const reject = (reason) => {  if (this.state === "pending") {  this.state = "rejected";  this.reason = reason;  this.onRejectedCallbacks.forEach((fn) => fn(reason));  }  };  try {  executor(resolve, reject);  } catch (e) {  reject(e);  }  }  then(onFulfilled, onRejected) {  const promise2 = new myPromise((resolve, reject) => {  if (this.state === "fulfilled") {  setTimeout(() => { // If ondepressing method fails, we will use trycatch to catch it try {  let x = onFulfilled(this.value); - reslove(x) + resolvePromise(promise,x,resolve,reject)  } catch (error) {  reject(error)  }  })  }  if (this.state === "rejected") {  setTimeout(() => {  try {  let x = onRejected(this.reason); - reslove(x) + resolvePromise(promise,x,resolve,reject)  } catch (error) {  reject(error)  }  })  }  if (this.state === "pending") {  this.onFulfilledCallbacks.push(value => {  try {  let x = onFulfilled(value) - reslove(x) + resolvePromise(promise,x,resolve,reject)  } catch (error) {  reject(error)  }  });  this.onRejectedCallbacks.push(reason => {  try {  let x = onRejected(reason) - reslove(x) + resolvePromise(promise,x,resolve,reject)  } catch (error) {  reject(error)  }  });  }  })  return promise2;  }  } + function resolvePromise(promise2,x,resovle,reject) { + resolve(x) +} Copy the code

We created a function resolvePromise specifically to handle resolve logic. Since this part is highly reusable, we split it into a single function. Currently, only resolve(x) is executed in this function.

When the return value of onFulfilled/onRejected is a Promise object

Observation shows that,

  1. Someone else’s promise normally passes the promise’s resolve value to the second then method
  2. Our promise, like an iron folly, returns the promise instance as a value

Here we have to understand the promise chain call, the specific implementation logic


Resolution:

  1. Usually, we call resolve to pass the ondepressing of the current THEN (onFulfilled,onRejected) and the return of onFulfilled
  2. This is a big pity. If onFulfilled and onRejected return is a promise instance, resolve will pass the onFullfilled/onRejected return of then(onFullfilled,onRejected) of the current instance

According to our understanding, to modify our family promise

class myPromise {
            constructor(executor) {
this.state = "pending"; // Internal states are pending,resolve,reject                this.value = null;
                this.reason = null;
 this.onFulfilledCallbacks = [];  this.onRejectedCallbacks = [];  const resolve = (value) => {  if (this.state === "pending") {  this.state = "fulfilled";  this.value = value;  this.onFulfilledCallbacks.forEach((fn) => fn(value));  }  };  const reject = (reason) => {  if (this.state === "pending") {  this.state = "rejected";  this.reason = reason;  this.onRejectedCallbacks.forEach((fn) => fn(reason));  }  };  try {  executor(resolve, reject);  } catch (e) {  reject(e);  }  }  then(onFulfilled, onRejected) {  const promise2 = new myPromise((resolve, reject) => {  if (this.state === "fulfilled") {  setTimeout(() => { // If ondepressing method fails, we will use trycatch to catch it try {  let x = onFulfilled(this.value);  resolvePromise(promise2, x, resolve, reject)  } catch (error) {  reject(error)  }  })  }  if (this.state === "rejected") {  setTimeout(() => {  try {  let x = onRejected(this.reason);  resolvePromise(promise2, x, resolve, reject)  } catch (error) {  reject(error)  }  })  }  if (this.state === "pending") {  this.onFulfilledCallbacks.push(value => {  try {  let x = onFulfilled(value)  resolvePromise(promise2, x, resolve, reject)  } catch (error) {  reject(error)  }  });  this.onRejectedCallbacks.push(reason => {  try {  let x = onRejected(reason)  resolvePromise(promise2, x, resolve, reject)  } catch (error) {  reject(error)  }  });  }  })  return promise2;  }  }  function resolvePromise(promise2, x, resolve, reject) { + if (x instanceof myPromise) { + if (x.state === 'pending') { + x.then((y) => { + resolvePromise(promise2, y, resolve, reject) + }, reject) + } else { + x.then(resolve, reject) +} + } else {  resolve(x) +}  } Copy the code

So let’s take a look at that


After observation, I found that our family promise is becoming more and more standardized

Special cases of special cases

When resolve returns an object or function that contains the THEN function, the then function is executed once

First let’s look at the comparison


We can see from observation that,

  1. Someone else’s promise is a successful return, fN1 entry
  2. And our promise returned the whole thing
  3. Someone else’s promise calls fn1 and fn2, but only executes the first one because fn1 and fn2 have the same single-execution principle as resolve/ Reject
  4. Quick question. What if fn1/fn2 in then returns a Promise object? Think about it, right?
  5. The bottom line is, we’re going to have to do it again, as promised

We perfected our promise according to our ideas

class myPromise {
            constructor(executor) {
this.state = "pending"; // Internal states are pending,resolve,reject                this.value = null;
                this.reason = null;
 this.onFulfilledCallbacks = [];  this.onRejectedCallbacks = [];  const resolve = (value) => {  if (this.state === "pending") {  this.state = "fulfilled";  this.value = value;  this.onFulfilledCallbacks.forEach((fn) => fn(value));  }  };  const reject = (reason) => {  if (this.state === "pending") {  this.state = "rejected";  this.reason = reason;  this.onRejectedCallbacks.forEach((fn) => fn(reason));  }  };  try {  executor(resolve, reject);  } catch (e) {  reject(e);  }  }  then(onFulfilled, onRejected) {  const promise2 = new myPromise((resolve, reject) => {  if (this.state === "fulfilled") {  setTimeout(() => { // If ondepressing method fails, we will use trycatch to catch it try {  let x = onFulfilled(this.value);  resolvePromise(promise2, x, resolve, reject)  } catch (error) {  reject(error)  }  })  }  if (this.state === "rejected") {  setTimeout(() => {  try {  let x = onRejected(this.reason);  resolvePromise(promise2, x, resolve, reject)  } catch (error) {  reject(error)  }  })  }  if (this.state === "pending") {  this.onFulfilledCallbacks.push(value => {  try {  let x = onFulfilled(value)  resolvePromise(promise2, x, resolve, reject)  } catch (error) {  reject(error)  }  });  this.onRejectedCallbacks.push(reason => {  try {  let x = onRejected(reason)  resolvePromise(promise2, x, resolve, reject)  } catch (error) {  reject(error)  }  });  }  })  return promise2;  }  }  function resolvePromise(promise2, x, resolve, reject) {  if (x instanceof myPromise) {  if (x.state === 'pending') {  x.then((y) => {  resolvePromise(promise2, y, resolve, reject)  }, reject)  } else {  x.then(resolve, reject)  } + } else if(x && (typeof x === 'object'|| typeof x === 'function')){ + let called = false; + try { + let then = x.then; + if(typeof then === 'function'){ + then.call(x,(y)=>{ + if(called) return; + called = true; + resolvePromise(promise2,y,resolve,reject); + },(e)=>{ + if(called) return; + called = true; + reject(e) +}) + }else{ + resolve(x) +} + } catch (error) { + if(called) return; + called = true; + reject(error) +} +}  else {  resolve(x)  }  } Copy the code

After that, let’s take a look at the results

Of course, returning a function with a THEN method is the same


You can see our family promise, once again, evolving into a promise on its own

Dealing with extremes

This is a big pity, onFulfilled, onFulfilled. Then ()

Let’s look at the comparison

Observation shows that

  1. Someone else’s promise was wrong
  2. Our family’s promise didn’t come back
  3. Our promise didn’t respond because it was stuck in an infinite loop of executing then

So let’s draw a picture

This is a normal situation


This ondepressing will be a self-loop when 2 returns the whole then


So here, let’s stop the self-loop

 class myPromise {
. }
 function resolvePromise(promise2, x, resolve, reject) {
+ if(promise2===x) {
+ Return Reject (new TypeError(' self loop ')) +}  if (x instanceof myPromise) { . }  } Copy the code

This is a big pity, onFulfilled,onRejected is not function

It’s a convention, so there’s no reason

Let’s add

 then(onFulfilled, onRejected) {
+ onFulfilled = typeof onFulfilled === 'function'? onFulfilled:y=>y;
+ onRejected = typeof onRejected === 'function'? onRejected:y=>{throw y; }
      let promise2 = new myPromise((resolve, reject) => {
Copy the code

When a promise’s reslove argument is a Promise

Temporarily do not know why, there is a big guy who knows this is also a convention, let’s take a look at an example


After observation, it can be found that the promise of resolve is the value of reslove, while the promise instance of reject is directly transmitted. Therefore, we will transform the promise of our family

 const resolve = (value) => {
+ if (value instanceof myPromise) {
+ return value.then(resolve, reject)
+}
                    if (this.state === "pending") {
 this.state = "fulfilled";  this.value = value;  this.onFulfilledCallbacks.forEach((fn) => fn(value));  }  }; Copy the code

Here, our family promise, something like this

class myPromise {
            constructor(executor) {
                this.state = "pending"; // Internal states are pending,resolve,reject
                this.value = null;
                this.reason = null;
 this.onFulfilledCallbacks = [];  this.onRejectedCallbacks = [];  const resolve = (value) = > {  if (value instanceof myPromise) {  return value.then(resolve, reject)  }  if (this.state === "pending") {  this.state = "fulfilled";  this.value = value;  this.onFulfilledCallbacks.forEach((fn) = > fn(value));  }  };  const reject = (reason) = > {  if (this.state === "pending") {  this.state = "rejected";  this.reason = reason;  this.onRejectedCallbacks.forEach((fn) = > fn(reason));  }  };  try {  executor(resolve, reject);  } catch (e) {  reject(e);  }  }  then(onFulfilled, onRejected) {  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : y= > y;  onRejected = typeof onRejected === 'function' ? onRejected : y= > { throw y; }  const promise2 = new myPromise((resolve, reject) = > {  if (this.state === "fulfilled") {  setTimeout((a)= > {  // If ondepressing method fails, we will use trycatch to catch it  try {  let x = onFulfilled(this.value);  resolvePromise(promise2, x, resolve, reject)  } catch (error) {  reject(error)  }  })   }  if (this.state === "rejected") {  setTimeout((a)= > {  try {  let x = onRejected(this.reason);  resolvePromise(promise2, x, resolve, reject)  } catch (error) {  reject(error)  }  })  }  if (this.state === "pending") {  this.onFulfilledCallbacks.push(value= > {  setTimeout((a)= > {  try {  let x = onFulfilled(value)  resolvePromise(promise2, x, resolve, reject)  } catch (error) {  reject(error)  }  })  });  this.onRejectedCallbacks.push(reason= > {  setTimeout((a)= > {  try {  let x = onRejected(reason)  resolvePromise(promise2, x, resolve, reject)  } catch (error) {  reject(error)  }  })  });  }  })  return promise2;  }  }  function resolvePromise(promise2, x, resolve, reject) {  if (x === promise2) {  return new TypeError('Self loop')  }  if (x instanceof myPromise) {  if (x.state === 'pending') {  x.then((y) = > {  resolvePromise(promise2, y, resolve, reject)  }, reject)  } else {  x.then(resolve, reject)  }  } else if (x && (typeof x === 'object' || typeof x === 'function')) {  let called = false;  try {  let then = x.then;  if (typeof then === 'function') {  then.call(x, (y) => {  if (called) return;  called = true;  resolvePromise(promise2, y, resolve, reject);  }, (e) => {  if (called) return;  called = true;  reject(e)  })  } else {  resolve(x)  }  } catch (error) {  if (called) return;  called = true;  reject(error)  }  }  else {  resolve(x)  }  }  Copy the code

Finally, let’s test our promise with Promises -aplus-tests

npm init

npm i promises-aplus-tests -D
Copy the code

Modify package.json

"scripts": {
// Follow your file    "test": "promises-aplus-tests ./src/index.js"
  },
Copy the code

perform

npm run test
Copy the code

It turned out that 40 of the tests failed

Boy, do you remember the hand that fell from the sky?

Here, other articles tell me that this is the promise. It’s like back in elementary school, when I asked my teacher a question and was told to just remember it. But, I refuse!!

Let’s take an extreme example


Why is it different?

At the root of the problem, the resolvePromise returned a fuifle promise


X.chen (reslove,reject) is executed directly within the resolvePromise() method inside the promise.


Custom ThEnable is returned as the value

So what do we do here

class myPromise {
    constructor(executor) {
this.state = "pending"; // Internal states are pending,resolve,reject        this.value = null;
        this.reason = null;
 this.onFulfilledCallbacks = [];  this.onRejectedCallbacks = [];  const resolve = (value) => {  if (value instanceof myPromise) {  return value.then(resolve, reject);  } + setTimeout(() => {  if (this.state === "pending") {  this.state = "fulfilled";  this.value = value;  this.onFulfilledCallbacks.forEach((fn) => fn(value));  } +})   };  const reject = (reason) => { + setTimeout(()=>{  if (this.state === "pending") {  this.state = "rejected";  this.reason = reason;  this.onRejectedCallbacks.forEach((fn) => fn(reason));  } +})  };  try {  executor(resolve, reject);  } catch (e) {  reject(e);  }  }  then(onFulfilled, onRejected) {  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : y => y;  onRejected = typeof onRejected === 'function' ? onRejected : y => { throw y; }  const promise2 = new myPromise((resolve, reject) => {  if (this.state === "fulfilled") {  setTimeout(() => { // If ondepressing method fails, we will use trycatch to catch it try {  let x = onFulfilled(this.value);  resolvePromise(promise2, x, resolve, reject)  } catch (error) {  reject(error)  }  })   }  if (this.state === "rejected") {  setTimeout(() => {  try {  let x = onRejected(this.reason);  resolvePromise(promise2, x, resolve, reject)  } catch (error) {  reject(error)  }  })  }  if (this.state === "pending") {  this.onFulfilledCallbacks.push(value => { - setTimeout(() => {  try {  let x = onFulfilled(value)  resolvePromise(promise2, x, resolve, reject)  } catch (error) {  reject(error)  } -})  });  this.onRejectedCallbacks.push(reason => { - setTimeout(() => {  try {  let x = onRejected(reason)  resolvePromise(promise2, x, resolve, reject)  } catch (error) {  reject(error)  } -})  });  }  })  return promise2;  } } function resolvePromise(promise2, x, resolve, reject) {  if (x === promise2) { Return Reject (New TypeError(' self loop ')) }  if (x instanceof myPromise) {  if (x.state === 'pending') {  x.then((y) => {  resolvePromise(promise2, y, resolve, reject)  }, reject)  } else {  x.then(resolve, reject)  }  } else if (x && (typeof x === 'object' || typeof x === 'function')) {  let called = false;  try {  let then = x.then;  if (typeof then === 'function') {  then.call(x, (y) => {  if (called) return;  called = true;  resolvePromise(promise2, y, resolve, reject);  }, (e) => {  if (called) return;  called = true;  reject(e)  })  } else {  resolve(x)  }  } catch (error) {  if (called) return;  called = true;  reject(error)  }  }  else {  resolve(x)  } } Copy the code

We’ve changed all resolve/ Reject to asynchronous firing

The last

We execute execute

npm run test
Copy the code

I’ve finally passed all the test cases

The final code

class myPromise {
  constructor(executor) {
    this.state = "pending"; // Internal states are pending,resolve,reject
    this.value = null;
    this.reason = null;
 this.onFulfilledCallbacks = [];  this.onRejectedCallbacks = [];  const resolve = (value) = > {  if (value instanceof myPromise) {  return value.then(resolve, reject);  }  setTimeout((a)= > {  if (this.state === "pending") {  this.state = "fulfilled";  this.value = value;  this.onFulfilledCallbacks.forEach((fn) = > fn(value));  }  });  };  const reject = (reason) = > {  setTimeout((a)= > {  if (this.state === "pending") {  this.state = "rejected";  this.reason = reason;  this.onRejectedCallbacks.forEach((fn) = > fn(reason));  }  });  };  try {  executor(resolve, reject);  } catch (e) {  reject(e);  }  }  then(onFulfilled, onRejected) {  onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (y) = > y;  onRejected =  typeof onRejected === "function"  ? onRejected  : (y) = > {  throw y;  };  const promise2 = new myPromise((resolve, reject) = > {  if (this.state === "fulfilled") {  setTimeout((a)= > {  // If ondepressing method fails, we will use trycatch to catch it  try {  let x = onFulfilled(this.value);  resolvePromise(promise2, x, resolve, reject);  } catch (error) {  reject(error);  }  });  }  if (this.state === "rejected") {  setTimeout((a)= > {  try {  let x = onRejected(this.reason);  resolvePromise(promise2, x, resolve, reject);  } catch (error) {  reject(error);  }  });  }  if (this.state === "pending") {  this.onFulfilledCallbacks.push((value) = > {  try {  let x = onFulfilled(value);  resolvePromise(promise2, x, resolve, reject);  } catch (error) {  reject(error);  }  });  this.onRejectedCallbacks.push((reason) = > {  try {  let x = onRejected(reason);  resolvePromise(promise2, x, resolve, reject);  } catch (error) {  reject(error);  }  });  }  });  return promise2;  } } function resolvePromise(promise2, x, resolve, reject) {  if (x === promise2) {  return reject(new TypeError("Self cycle"));  }  if (x instanceof myPromise) {  if (x.state === "pending") {  x.then((y) = > {  resolvePromise(promise2, y, resolve, reject);  }, reject);  } else {  x.then(resolve, reject);  }  } else if (x && (typeof x === "object" || typeof x === "function")) {  let called = false;  try {  let then = x.then;  if (typeof then === "function") {  then.call(  x,  (y) => {  if (called) return;  called = true;  resolvePromise(promise2, y, resolve, reject);  },  (e) => {  if (called) return;  called = true;  reject(e);  }  );  } else {  resolve(x);  }  } catch (error) {  if (called) return;  called = true;  reject(error);  }  } else {  resolve(x);  } }  myPromise.deferred = function () {  let defer = {};  defer.promise = new myPromise((resolve, reject) = > {  defer.resolve = resolve;  defer.reject = reject;  });  return defer; };  module.exports = myPromise; Copy the code

This article is formatted using MDNICE