Small knowledge, big challenge! This paper is participating in theEssentials for programmers”Creative activities.

This article also participated in the “Digitalstar Project” to win a creative gift package and creative incentive money

Promise core logic implementation

The goal of this article is to understand the principle of Promise by breaking it down a little bit, so that we can achieve the purpose of handwritten Promise

Need to sort out

Before we hand off the code, we need to sort out how to use the Promise, some features of the Promise, convenient for us to better code writing, so we have to know what is the Promise!

1. CallPromise

To invoke a Promise, we need to create a Promise object, starting with the new keyword

new Promise(a)Copy the code

From this code we can see that Promise is a class

2. The parameters

We need to pass in a callback function called an executor that will execute immediately, meaning that when we execute a new Promise(fn), the fn in the function will be called immediately

new Promise(() = >{})Copy the code

3. Actuator parameters

When we pass a callback, we get resolve and reject, which are essentially two functions that change the Promise state.

new Promise((resolve,reject) = >{})Copy the code

4.PromiseThe state of the

There are three states in Promise, which are:

  • successfulfulfilled
  • failurerejected
  • Waiting for thepending

5.PromiseState change characteristics

A Promise can only change its state in one of two ways:

  • Waiting for the transition to success (pending => fulfilled
  • Waiting for the transition to failure (pending => rejected

[Note:] Once the status is determined, it cannot be changed

6. How can I modify itPromiseThe state? Passing successful or failed values?

You can modify the resolve and reject state. Resolve:

new Promise((resolve,reject) = > {
    resolve('success')})Copy the code

Reject:

new Promise((resolve,reject) = >{reject(reject)}Copy the code

7. Then method

Here’s a link: promise.prototype.then () take a look at this link and let’s sort out the requirements

The then() method returns a Promise (en-us). It needs at most two arguments: a callback to Promise’s success and failure cases.

That is, what the then method does internally is determine the state, call different functions depending on the state, and each Promise can call the THEN method, which means that the method is on the prototype chain

let promise = new Promise((resolve,reject) = >{reject(reject)}) promise.() = >{}, () = >{})
Copy the code

8. Callback parameters for then

Let’s start with the following code

let promise = new Promise((resolve,reject) = >{reject(reject)}// promise.then(onFulfilled[, onRejected]);
promise.then(value= > { // fulfillment }, reason => { // rejection});
Copy the code

Based on the link to the document I posted earlier, we can conclude that the success and failure callbacks of then have a success/failure value as an argument

Need to sort out

To sort out our requirements:

  • PromiseIs a class. To execute this class, we need to pass in a callback function that will execute immediately
  • PromiseThe callback function in theresolvewithreject, these two arguments are actually two functions
  • PromiseThere are three states respectively:
    • successfulfulfilled
    • failurerejected
    • Waiting for thepending
  • PromiseOnly throughresolvewithrejectState changes are made and data is passed through these two functions
  • PromiseOnce completed, the state change cannot be modified andPromiseThere can only be two cases of state change:
    • Waiting for the transition to success (pending => fulfilled
    • Waiting for the transition to failure (pending => rejected
  • PromisethethenMethods call different functions depending on the state and all methods are on the prototype chain
  • PromisethethenMethod success and failure callbacks, each with a success/failure value as an argument

Begin to write

1. PromiseIs a class

It was a simple first step, a small step for Promise, a giant step for me as a crayon programmer

class MyPromise {}Copy the code

2. You need to pass in a callback function

We receive this using constructor, a special method for creating and initializing objects created by the class.

class MyPromise {
    constructor(callback){}}Copy the code

3. The callback function is executed immediately

class MyPromise {
    constructor(callback) {
        callback()
    }
}
Copy the code

4. PromiseThe callback function has two arguments to the functionresolvewithreject

Here we need to pass resolve and reject into the callback and define resolve and Reject functions in MyPromise, which we define as arrow functions

class MyPromise {... resolve =() = > {
    
    }
    reject = () = >{}}Copy the code

Q: A:So let’s remember how do we call these two functions

new Promise((resolve,reject) = > {
    resolve('success')})Copy the code

or

new Promise((resolve,reject) = >{reject(reject)}Copy the code

Is it always called directly like this? If this function is a normal function, does this refer to window or undefined?

Let’s experiment

Ordinary functions:

Arrow function: Here’s a review:

Normal functions have their this pointer determined when called, whereas arrow functions have their this pointer determined when defined!

5.PromiseThere are three states in

We need to define the constant of the state, the variable of the state value to change and make it equal to pending by default

const PENDING = 'pending' / / wait for
const FULFILLED = 'fulfilled' / / success
const REJECTED = 'reject' / / fail
class MyPromise {... }Copy the code

Q: What are the benefits of defining constants?

A: ahem! Mainly for code reuse (vibrato)

6.PromisethroughresolvewithrejectState changes are made and data is passed through these two functions

This is where we need to modify resolve and Reject

const PENDING = 'pending' / / wait for
const FULFILLED = 'fulfilled' / / success
const REJECTED = 'reject' / / fail
class MyPromise {
    / / Promise
    status = PENDING
    // Success value
    value = undefined
    // Failed value
    reason = undefined
    constructor(callback) {
        callback(this.resolve,this.reject)
    }
    resolve = value= > {
        // Change the status to success
        this.status = FULFILLED
        // Save the successful value
        this.value = value
    }
    reject = value= > {
        // Change the status to failed
        this.status = REJECTED
        // Save the failed value
        this.reason = value
    }
}
Copy the code

7.PromiseThere can only be two cases of state change in the

Here, we mainly modify resolve and reject. We need to determine the current status of status and whether it is waiting. If it is in the modified state, if it is not, it cannot be modified

const PENDING = 'pending' / / wait for
const FULFILLED = 'fulfilled' / / success
const REJECTED = 'reject' / / fail
class MyPromise {... resolve =() = > {
        // If the state is not wait, prevent the program from executing down
        if(this.status ! == PENDING){return
        }
        // Change the status to success
        this.status = FULFILLED
        // Save the successful value
        this.value = value
    }
    reject = () = > {
        // If the state is not wait, prevent the program from executing down
        if(this.status ! == PENDING){return
        }
        // Change the status to failed
        this.status = REJECTED
        // Save the failed value
        this.reason = value
    }
}
Copy the code

8.PromisethethenMethods call different functions depending on the state and all methods are on the prototype chain

Then we need to define the THEN method in the class and determine whether the successful callback is called if the state is successful and the failed callback is called if the state is unsuccessful

const PENDING = 'pending' / / wait for
const FULFILLED = 'fulfilled' / / success
const REJECTED = 'reject' / / fail
class MyPromise {...then( successCallback, failCallback) {
        // If the status is successful
        if(this.status === FULFILLED){
            successCallback()
        }else if(this.status === REJECTED){
            failCallback()
        }
    }
}
Copy the code

9.PromisethethenMethod success and failure callbacks, each with a success/failure value as an argument

We need to pass the success/failure value back in the then

const PENDING = 'pending' / / wait for
const FULFILLED = 'fulfilled' / / success
const REJECTED = 'reject' / / fail
class MyPromise {...then( successCallback, failCallback) {
        // If the status is successful
        if(this.status === FULFILLED){
            successCallback(this.value)
        }else if(this.status === REJECTED){
            failCallback(this.reason)
        }
    }
}
Copy the code

This is where our core module is implemented and tested

Promise core logic implementation summary

On the blackboard On the blackboard On the blackboard

  • PromiseIs a class. To execute this class, we need to pass in a callback function that will execute immediately
  • PromiseThe callback function in theresolvewithreject, these two arguments are actually two functions
  • PromiseThere are three states respectively:
    • successfulfulfilled
    • failurerejected
    • Waiting for thepending
  • PromiseOnly throughresolvewithrejectChange the state and save the state with these two values
  • PromiseOnce completed, the state change cannot be modified andPromiseThere can only be two cases of state change:
    • Waiting for the transition to success (pending => fulfilled )
    • Waiting for the transition to failure (pending => rejected )
  • PromisethethenMethods call different functions depending on the state and all methods are on the prototype chain
  • PromisethethenMethod success and failure callbacks, each with a success/failure value as an argument

The complete code

const PENDING = 'pending' / / wait for
const FULFILLED = 'fulfilled' / / success
const REJECTED = 'reject' / / fail
class MyPromise {
    / / Promise
    status = PENDING
    // Success value
    value = undefined 
    // Failed value
    reason = undefined
    constructor(callback) {
        callback(this.resolve,this.reject)
    }
    resolve = value= > {
        // If the state is not wait, prevent the program from executing down
        if(this.status ! == PENDING){return
        }
        // Change the status to success
        this.status = FULFILLED
        // Save the successful value
        this.value = value
    }
    reject = value= > {
        // If the state is not wait, prevent the program from executing down
        if(this.status ! == PENDING){return
        }
        // Change the status to failed
        this.status = REJECTED
        // Save the failed value
        this.reason = value
    }
    then( successCallback, failCallback) {
        // If the status is successful
        if(this.status === FULFILLED){
            successCallback(this.value)
        }else if(this.status === REJECTED){
            failCallback(this.reason)
        }
    }
}
Copy the code

PromiseAdd asynchronous logic

As we all know, in Promise, we mostly solved some of the problems of making asynchronous calls, but, as far as our code is concerned, there are still some problems with using asynchro

First of all, according to the requirements of our last article:

Promise is a class that, when implemented, passes in a callback function that executes immediately

Then look at the following implementation

let promise1 = new MyPromise((resolve,reject) = > {
    setTimeOut(() = >{
        resolve(1)},2000)})Copy the code

Our callback function will execute immediately, but the resolve method to change the state is included in the asynchronous task, causing our THEN module to call into confusion.

then( successCallback, failCallback) {
    // If the status is successful
    if(this.status === FULFILLED){
        successCallback(this.value)
    }else if(this.status === REJECTED){
        failCallback(this.reason)
    }
}
Copy the code

The then module’s current mood

So we’re going to do a pairthenModification of method

Modified then method

  1. We need to determine what happens when we add waits
    • Solution: Add an else judgment
  2. In the case of waiting, we want to temporarily store the failed/successful callback
    • Solution: Add an instance property to store successful/failed callbacks

Code implementation

const PENDING = 'pending' / / wait for
const FULFILLED = 'fulfilled' / / success
const REJECTED = 'reject' / / fail
class MyPromise {...// Successful callback
    successCallback = undefined
    // Failed callback
    failCallback = undefined.then( successCallback, failCallback) {
        // If the status is successful
        if(this.status === FULFILLED){
            successCallback(this.value)
        }else if(this.status === REJECTED){
            failCallback(this.reason)
        }else{
        // The waiting condition
            this.successCallback = successCallback
            this.failCallback = failCallback
        }
    }
}
Copy the code

But it does not solve the problem of asynchronous function execution.

Keep reading!

A callback that completes asynchronous execution

In async, we still execute resolve and Reject, so we just call our saved methods in resolve and Reject

Code implementation

resolve = value= > {
    // If the state is not wait, prevent the program from executing down
    if(this.status ! == PENDING){return
    }
    // Change the status to success
    this.status = FULFILLED
    // Save the successful value
    this.value = value
    // Determine if the successful callback exists, and if so, call
    this.successCallback && this.successCallback(this.value)
}
reject = value= > {
    // If the state is not wait, prevent the program from executing down
    if(this.status ! == PENDING){return
    }
    // Change the status to failed
    this.status = REJECTED
    // Save the failed value
    this.reason = value
    // Determine if the failed callback exists, and if so, invoke
    this.failCallback && this.failCallback(this.reason)
}
Copy the code

Ok, so we’re done here

Test the

const MyPromise = require('./myPromise')
let promise1 = new MyPromise((resolve,reject) = > {
  setTimeout(() = >{
    resolve('success')
  })
})
promise1.then(value= >{
  console.log('success',value)
},reasons= > {
  console.log('failure',reasons)
})
Copy the code

Yes, no problem

This completes the processing of asynchronous functions and moves on to the next chapter

The complete code

const PENDING = 'pending' / / wait for
const FULFILLED = 'fulfilled' / / success
const REJECTED = 'reject' / / fail
class MyPromise {
  / / Promise
  status = PENDING
  // Success value
  value = undefined
  // Failed value
  reason = undefined
  // Successful callback
  successCallback = undefined
  // Failed callback
  failCallback = undefined
  constructor (callback) {
    callback(this.resolve, this.reject)
  }
  resolve = value= > {
    // If the state is not wait, prevent the program from executing down
    if (this.status ! == PENDING) {return
    }
    // Change the status to success
    this.status = FULFILLED
    // Save the successful value
    this.value = value
    // Determine if the successful callback exists, and if so, call
    this.successCallback && this.successCallback(this.value)
  }
  reject = value= > {
    // If the state is not wait, prevent the program from executing down
    if (this.status ! == PENDING) {return
    }
    // Change the status to failed
    this.status = REJECTED
    // Save the failed value
    this.reason = value
    // Determine if the failed callback exists, and if so, invoke
    this.failCallback && this.failCallback(this.reason)
  }
  then (successCallback, failCallback) {
    // If the status is successful
    if (this.status === FULFILLED) {
      console.log('test')
      successCallback(this.value)
    } else if (this.status === REJECTED) {
      failCallback(this.reason)
    } else {
      // The waiting condition
      this.successCallback = successCallback
      this.failCallback = failCallback
    }
  }
}
Copy the code

PromiseImplement the then method multiple calls to add multiple handlers

Ask questions

Now there is a new problem, as in the code below, we can execute promise1 multiple times in asynchronous mode, and output it multiple times

The test code

let promise1 = new Promise((resolve,reject) = > {
  setTimeout(() = >{
    resolve('success')},2000)})/ / for the first time
promise1.then(value= >{
  console.log('success',value)
},reasons= > {
  console.log('failure',reasons)
})
/ / the second time
promise1.then(value= >{
  console.log('success',value)
},reasons= > {
  console.log('failure',reasons)
})
/ / the third time
promise1.then(value= >{
  console.log('success',value)
},reasons= > {
  console.log('failure',reasons)
})
Copy the code

Write a classmyPromisetest

The class we implemented couldn’t do it! How to do?

What difficulties we encounter, don’t be afraid, smile in the face of it, the best way to eliminate fear is to face fear, persistence is victory, come on, Ollie give!

Find the problem

First of all, let’s address the problem, why do we only execute once? After we looked through the code, we found the cause

then (successCallback, failCallback) {
    // If the status is successful
    if (this.status === FULFILLED) {
      successCallback(this.value)
    } else if (this.status === REJECTED) {
      failCallback(this.reason)
    } else {
      // The waiting condition
      this.successCallback = successCallback
      this.failCallback = failCallback
    }
  }
Copy the code

Why is this happening

First, we don’t store all the time we call then, so if we call it asynchronously, successCallback and failCallback will store the value of the last time we did it. As a result, other values are not executed, as shown below:

Ps:Synchronization and no problem, you can test it

The solution to the problem

Locating the root of the disease, then our solution is very convenient:

  • transformthenThe storage of the failed/successful methods of the function => can be changed to an array for storage
  • transformresolvewithrejectsuccessCallbackandfailCallbackThe execution mode => loop over the stored array and then execute

transformthenfunction

SuccessCallback and failCallback are changed to arrays

class MyPromise {...// Successful callback
  successCallback = []
  // Failed callback
  failCallback = []
  ...
  then (successCallback, failCallback) {
    // If the status is successful
    if (this.status === FULFILLED) {
      console.log('test')
      successCallback(this.value)
    } else if (this.status === REJECTED) {
      failCallback(this.reason)
    } else {
      // The waiting condition
      this.successCallback.push(successCallback)
      this.failCallback.push(failCallback)
    }
  }
 }
Copy the code

Ok, here and the modification is complete!

transformresolvewithrejectfunction

Resolve:

resolve = value= > {
    // If the state is not wait, prevent the program from executing down
    if (this.status ! == PENDING) {return
    }
    // Change the status to success
    this.status = FULFILLED
    // Save the successful value
    this.value = value
    // Determine if the successful callback exists, and if so, call
    // this.successCallback && this.successCallback(this.value)
    while(this.successCallback.length){
        // Call the Shift method and each time it executes, push the method out of the array stack and return the data back
        this.successCallback.shift()(this.value)
    }
  }
Copy the code

Reject:

reject = value= > {
    // If the state is not wait, prevent the program from executing down
    if (this.status ! == PENDING) {return
    }
    // Change the status to failed
    this.status = REJECTED
    // Save the failed value
    this.reason = value
    // Determine if the failed callback exists, and if so, invoke
    // this.failCallback && this.failCallback(this.reason)
    while(this.failCallback.length){
        // Call the Shift method and each time it executes, push the method out of the array stack and return the data back
        this.failCallback.shift()(this.reason)
    }
  }
Copy the code

So we’re done here

test

Now that we’re done, how else can we verify that we’re right

The test case

const myPromise = require('./myPromise')

let promise1 = new myPromise((resolve, reject) = > {
  setTimeout(() = >{
    reject('failure')},1000)
})
promise1.then(value= >{
  console.log(value)
},reason= > {
  console.log(1)
  console.log(reason)
})
promise1.then(value= >{
  console.log(value)
},reason= > {
  console.log(2)
  console.log(reason)
})
promise1.then(value= >{
  console.log(value)
},reason= > {
  console.log(3)
  console.log(reason)
})
Copy the code

The test results

Perfect, other use cases will not take screenshots, too many, you are interested in testing it yourself!

conclusion

The main purpose of this summary is to solve the problem that the then method under Promise can be called multiple times. When its state changes, the internal methods are executed in sequence, and the synchronous and asynchronous cases need to be distinguished

  • In the asynchronous case, we need to store methods and call them in turn
  • In the case of synchronization, we just need to call the corresponding method based on the state

The complete code

const PENDING = 'pending' / / wait for
const FULFILLED = 'fulfilled' / / success
const REJECTED = 'reject' / / fail
class MyPromise {
  / / Promise
  status = PENDING
  // Success value
  value = undefined
  // Failed value
  reason = undefined
  // Successful callback
  successCallback = []
  // Failed callback
  failCallback = []
  constructor (callback) {
    callback(this.resolve, this.reject)
  }
  resolve = value= > {
    // If the state is not wait, prevent the program from executing down
    if (this.status ! == PENDING) {return
    }
    // Change the status to success
    this.status = FULFILLED
    // Save the successful value
    this.value = value
    // Determine if the successful callback exists, and if so, call
    // this.successCallback && this.successCallback(this.value)
    while(this.successCallback.length){
        // Call the Shift method and each time it executes, push the method out of the array stack and return the data back
        this.successCallback.shift()(this.value)
    }
  }
  reject = value= > {
    // If the state is not wait, prevent the program from executing down
    if (this.status ! == PENDING) {return
    }
    // Change the status to failed
    this.status = REJECTED
    // Save the failed value
    this.reason = value
    // Determine if the failed callback exists, and if so, invoke
    // this.failCallback && this.failCallback(this.reason)
    while(this.failCallback.length){
        // Call the Shift method and each time it executes, push the method out of the array stack and return the data back
        this.failCallback.shift()(this.reason)
    }
  }
  then (successCallback, failCallback) {
    // If the status is successful
    if (this.status === FULFILLED) {
      successCallback(this.value)
    } else if (this.status === REJECTED) {
      failCallback(this.reason)
    } else {
      // The waiting condition
      this.successCallback.push(successCallback)
      this.failCallback.push(failCallback)
    }
  }
}
Copy the code

PromiseimplementationthenMethod

Need to sort out

The then methods below Promise can be called chained, and the return value of the next THEN method is actually the return value of the callback function of the previous THEN method. Let’s look at a few cases

1. The first function is called without a return value:

Code:

let promise1 = new Promise((resolve, reject) = > {
  resolve('success')
})
promise1.then(val= > {
  console.log(val)
}).then(val= >{
  console.log(val)
})
Copy the code

Results:

2. The first function to be chained returns a value:

Code:

let promise1 = new Promise((resolve, reject) = > {
  resolve('success')
})
promise1.then(val= > {
  console.log(val)
  return 100
}).then(val= >{
  console.log(val)
})
Copy the code

Results:

So you can be sure that the return value of the next THEN method is actually the return value of the callback function of the previous THEN method

So we need to do two things:

  1. thenMethod
  2. Pass the return value of the previous one to the nextthenmethods

implementationthenMethod

First, we need to make it clear that then is under a Promise. If we want to implement chained calls to then methods, each then method should return a Promise object. If then returns a Promise object, then we can make chained calls easier.

The first step

Since we’re returning a Promise, we return a Promise in the then method

then (successCallback, failCallback) {
    let promiseThen = new MyPromise()
    // If the status is successful
    if (this.status === FULFILLED) {
      successCallback(this.value)
    } else if (this.status === REJECTED) {
      failCallback(this.reason)
    } else {
      // The waiting condition
      this.successCallback.push(successCallback)
      this.failCallback.push(failCallback)
    }
    return promiseThen
  }
Copy the code

The second step

Now that it has returned, do we need to modify the judgment a little bit? Let’s recall that when we execute a new Promise, we can pass in a callback function that executes immediately!

Well, I have a half-baked idea! Pass our internal then judgments into MyPromise to execute!

then (successCallback, failCallback) {
    let promiseThen = new MyPromise(() = > {
        // If the status is successful
        if (this.status === FULFILLED) {
            successCallback(this.value)
        } else if (this.status === REJECTED) {
            failCallback(this.reason)
        } else {
            // The waiting condition
            this.successCallback.push(successCallback)
            this.failCallback.push(failCallback)
        }
    })
    return promiseThen
}
Copy the code

At this point our chained call to the THEN method is written

thenMethod value passing

First, we need to save the return value of the execution. This is very simple, we just need to save the return value of the execution

let x = successCallback(this.value)
Copy the code

So the next question is, how do you pass the data?

Don’t worry, let’s review again!

Promise uses resolve and Reject to change state, and data is passed through these two functions

We can then pass the return value to the promise data we return by calling resolve, which in turn passes the return value of the previous one to the next THEN method

let promiseThen = new MyPromise((resolve, reject) = > {
    // If the status is successful
    if (this.status === FULFILLED) {
        let x = successCallback(this.value)
        resolve(x)
    } else if (this.status === REJECTED) {
        failCallback(this.reason)
    } else {
        // The waiting condition
        this.successCallback.push(successCallback)
        this.failCallback.push(failCallback)
    }
})
return promiseThen
Copy the code

Is it over? Don’t have not

At this point our judgment seems to be over, but! We’re not done yet. What if you return a Promise object? What if the object succeeds or fails? Roll up your sleeves and work hard!

 if (this.status === FULFILLED) {
        let x = successCallback(this.value)
        resolve(x)
    }
Copy the code

Resolve (x) cannot be written this way! I’m going to make some changes, add some judgments, what judgments?

  • Check if x is a Promise object
  • If you don’t call reslove directly
  • If it is a Promise object, the call is selected based on its resultsresolveorrejectfunction

Considering the reusability of the code, we extract this statement

// Resolve whether the promise function is available
// Instead of calling reslove directly
// Call reslove or reject as appropriate
function resolvePromise(x, resolve, reject){
  if(x instanceof MyPromise){
    x.then(resolve, reject)
  }else{
    resolve(x)
  }

}
Copy the code

Then modify the then

then (successCallback, failCallback) {
    let promiseThen = new MyPromise((resolve, reject) = > {
      // If the status is successful
      if (this.status === FULFILLED) {
        let x = successCallback(this.value)
        resolvePromise(x, resolve, reject)
      }
      ...
    })
    return promiseThen
  }
Copy the code

This way we have handled the synchronous case, of course we also need to handle the asynchronous case!

The idea is the same

.// The waiting condition
this.successCallback.push(() = > {
  try {
    let x = successCallback(this.value)
    resolvePromise(x, resolve, reject)
  } catch (e) {
    reject(e)
  }
})
this.failCallback.push(() = > {
  try {
    let x = failCallback(this.reason)
    resolvePromise(x, resolve, reject)
  } catch (e) {
    reject(e)
  }

})
Copy the code

Here we add catch to prevent the execution from failing, of course the previous execution also needs to add OK to enter the test mode

test

Test synchronous chain calls

const myPromise = require('./myPromise')

let promise = new myPromise((resolve, reject) = > {
  resolve('success')})let promise1 = new myPromise((resolve, reject) = > {
  resolve('1' success)
})
promise.then(val= > {
  console.log('First time',val)
  return promise1
}).then(val= >{
  console.log('The second time',val)
  return promise
}).then(val= >{
  console.log('Third time',val)
  return promise1
}).then(val= >{
  console.log('The fourth time',val)
})
Copy the code

Expected output:

  • First success
  • Success the second time 1
  • Third time successful
  • The fourth success 1

Synchronous chain call test results

Asynchronous chain call tests

const myPromise = require('./myPromise')

let promise = new myPromise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('Successful 1S delay version')},1000)})let promise1 = new myPromise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('Success 0.5 Delayed version')},500)
})
promise.then(val= > {
  console.log('First time',val)
  return promise1
}).then(val= >{
  console.log('The second time',val)
  return promise
}).then(val= >{
  console.log('Third time',val)
  return promise1
}).then(val= >{
  console.log('The fourth time',val)
})
Copy the code

Expected output:

  • The first successful 1s delay version
  • Second success 0.5 delayed version
  • Third successful 1s delay version
  • The fourth success 0.5 delayed version

Asynchronous chain call test results

It’s all ok

conclusion

The main reason for the chain call of then is that we return the same class, so that the function can continue to call then methods. We should pay attention to the asynchronous and synchronous call of function when we write, and also pay attention to our method selection.

PromisethethenMethod chain calls recognize that the Promise object returns itself

We can return an object when we use the then method, but if we return the original Promise, there will be a thing, and the Promise will be called in a loop, and the system will bug, and we want to avoid that, and we want to demonstrate that error

Use systempromiseObject to simulate an error

let promise = new Promise((resolve, reject) = > {
    resolve(100)})let promise1 = promise.then((value) = >{
    console.log(value)
    return promise1
})
Copy the code

The results of

Ok, so that’s where our requirements come in, how do we handle this case of calling ourselves?

implementation

It’s actually pretty simple, so let’s go to the then method, remember our code before? Let x = successCallback(this.value) let x = successCallback(this.value) let x = successCallback(this.value) let x = successCallback(this.value) We choose resolvePromise for this transformation

resolvePromiseThe reconstruction of

ResolvePromise adds an entry to thenPromise, and then throws an error if the promise is the same

function resolvePromise (thenPromise,returnValue, resolve, reject) {
    if(thenPromise === returnValue){
        return reject(new TypeError('Chaining cycle detected for promise #<MyPromise>'))}if (returnValue instanceof MyPromise) {
        returnValue.then(resolve, reject)
    } else {
        resolve(returnValue)
    }
}
Copy the code

Then change the place where the method is called and add a pass parameter.

But the problem is not so easy!

So let’s look at the code here

let thenPromise = new MyPromise((resolve, reject) = >{...let x = successCallback(this.value)
   resolvePromise(thenPromise, x, resolve, reject)
   ...
})
Copy the code

Here we need to pass the thenPromise (resolvePromise, x, resolve, reject), but thenPromise is declared after the new MyPromise has finished. What to do?? Actually also simple! Asynchronous call!! SetTimeout !!!! Did it all come out right away? Start reinventing!

After you change the paste code set

test

The test code

const myPromise = require('./myPromise')

let promise = new myPromise((resolve, reject) = > {
    resolve('success')})let promise1 = promise.then(val= > {
  console.log(val)
  return promise1
})
promise1.then(val= >{},reason= > {
  console.log(reason.message)
})
Copy the code

The test results

perfect

thenMethod parameters become optional

let promise3 = new Promise((resolve, reject) = > {
  resolve('failure')
})
promise3.then().then().then( value= > console.log(value))
Copy the code

A Promise is a case where you can keep passing parameters until there is a callback method

How do you do that? Value => value; value => value; value => value; Perfect utilization characteristic

In the code

// Create a variable
const isFunction = (value) = > typeof value === "function"; . .then(successCallback, failCallback) {
    successCallback = isFunction(successCallback)
      ? successCallback
      : (value) = > value;
    failCallback = isFunction(failCallback)
      ? failCallback
      : (err) = > {
          throwerr; }; . }...Copy the code

Test the

The test case

const myPromise = require('./myPromise')

let promise = new myPromise((resolve, reject) = > {
  setTimeout(() = > {
    resolve('success')},2000)})let promise1 = new myPromise((resolve, reject) = > {
  setTimeout(() = > {
    reject('failure')},2000)
})
promise.then().then().then( value= > console.log(value,1), value= > console.log(value,2))
promise1.then().then().then( value= > console.log(value,3), value= > console.log(value,4))
Copy the code

Expected output:

  • Success 1
  • Failure of 4

The test results

The complete code

const PENDING = "pending"; / / wait for
const FULFILLED = "fulfilled"; / / success
const REJECTED = "reject"; / / fail
const isFunction = (value) = > typeof value === "function";
class MyPromise {
  / / Promise
  status = PENDING;
  // Success value
  value = undefined;
  // Failed value
  reason = undefined;
  // Successful callback
  successCallback = [];
  // Failed callback
  failCallback = [];
  // Errors need to be caught in the actuator
  constructor(callback) {
    try {
      callback(this.resolve, this.reject);
    } catch (e) {
      this.reject(e);
    }
  }
  resolve = (value) = > {
    // If the state is not wait, prevent the program from executing down
    if (this.status ! == PENDING) {return;
    }
    // Change the status to success
    this.status = FULFILLED;
    // Save the successful value
    this.value = value;
    // Determine if the successful callback exists, and if so, call
    // this.successCallback && this.successCallback(this.value)
    while (this.successCallback.length) {
      // Call the Shift method and each time it executes, push the method out of the array stack and return the data back
      this.successCallback.shift()(); }}; reject =(value) = > {
    // If the state is not wait, prevent the program from executing down
    if (this.status ! == PENDING) {return;
    }
    // Change the status to failed
    this.status = REJECTED;
    // Save the failed value
    this.reason = value;
    // Determine if the failed callback exists, and if so, invoke
    // this.failCallback && this.failCallback(this.reason)
    while (this.failCallback.length) {
      // Call the Shift method and each time it executes, push the method out of the array stack and return the data back
      this.failCallback.shift()(); }};then(successCallback, failCallback) {
    successCallback = isFunction(successCallback)
      ? successCallback
      : (value) = > value;
    failCallback = isFunction(failCallback)
      ? failCallback
      : (err) = > {
          throw err;
        };
    let thenPromise = new MyPromise((resolve, reject) = > {
      // If the status is successful
      if (this.status === FULFILLED) {
        setTimeout(() = > {
          try {
            let x = successCallback(this.value);
            // Determine if x is MyPromise
            // If yes, go straight
            resolvePromise(thenPromise, x, resolve, reject);
          } catch(e) { reject(e); }},0);
        // resolve(x)
      } else if (this.status === REJECTED) {
        setTimeout(() = > {
          try {
            let x = failCallback(this.reason);
            resolvePromise(thenPromise, x, resolve, reject);
          } catch(e) { reject(e); }},0);
      } else {
        // The waiting condition
        this.successCallback.push(() = > {
          setTimeout(() = > {
            try {
              let x = successCallback(this.value);
              resolvePromise(thenPromise, x, resolve, reject);
            } catch(e) { reject(e); }},0);
        });
        this.failCallback.push(() = > {
          setTimeout(() = > {
            try {
              let x = failCallback(this.reason);
              resolvePromise(thenPromise, x, resolve, reject);
            } catch(e) { reject(e); }},0); }); }});returnthenPromise; }}// Resolve whether the promise function is available
// Instead of calling reslove directly
// Call reslove or reject as appropriate
function resolvePromise(thenPromise, value, resolve, reject) {
  if (thenPromise === value) {
    return reject(
      new TypeError("Chaining cycle detected for promise #<MyPromise>")); }if (value instanceof MyPromise) {
    value.then(resolve, reject);
  } else{ resolve(value); }}module.exports = MyPromise;

Copy the code

Promisetheallmethods

All is designed to solve the problem of asynchronous concurrency by allowing us to get the results of asynchronous code execution in the order in which it is called

example

function promise () {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve('1' success)},2000)})}function promise1 () {
  return new Promise((resolve, reject) = > {
    resolve('1' success)})}Copy the code

There are two functions, if we call promise and promise1 at the same time, we get the code for promise1 first and then the promise value, but promise. all gets the execution order of promise and promise1. How does this work?

Let’s look at parameters and return values and so on

parameter

  • 可迭代

    An iterable, such as Array or String.

The return value

  • If the argument passed is an empty iterable, return a Promise that is already resolved.
  • If the parameter passed does not contain anypromise, returns oneAsynchronously Resolved  Promise. Note: Google Chrome 58 returns one in this caseAlready resolvedThe state of thePromise.
  • Otherwise return onePendingPromise. This returnspromiseAnd then there will be at allpromiseBoth completed or have onepromiseWhen the failureasynchronousTo complete or fail. See below for an example of “asynchrony or synchronization for Promise.all”. The return value will be the same as that in the argumentpromiseOrder, not by callpromiseTo determine the order of completion.

instructions

This method is useful when gathering the return results of multiple promises.

Fulfillment: If the iterable passed in is empty, promise.all returns a Promise in a completed state synchronously. The promise returned by promise.all becomes completed asynchronously if all incoming promises become complete, or if there are no promises in the incoming iterable. In any case, the result of the completion state of the Promise returned by promise.all is an array containing the values of all the passed iteration parameter objects (including non-Promise values).

Rejection: If a promise is passed in with a failed promise, promise. all asynchronously gives that failed result to the failed state callback, regardless of whether the other promise is fulfilled or not.

The sample

var p1 = Promise.resolve(3);
var p2 = 1337;
var p3 = new Promise((resolve, reject) = > {
  setTimeout(resolve, 100.'foo');
});

Promise.all([p1, p2, p3]).then(values= > {
  console.log(values); // [3, 1337, "foo"]
});
Copy the code

implementation

See the use example above, promise.all, so the all method must be a static method

Ok, open the whole

1. Add static methodsall

Add static methods to myPromise and parameters to it (of type array)

static all (array) {
  
}
Copy the code

2. The return value

All returns a Promise object. All returns a Promise object. So!

static all (array) {
    return new MyPromise((resolve, reject) = >{})}Copy the code

3. Determine the type of the value

So if it’s a Promise object, we’ll just put it in the array of the result value, and if it’s a Promise object, we’ll put it in the array of the result value after we execute, okay

static all (array) {
    return new MyPromise((resolve, reject) = >{
      for(const i in array) {
        let current = array[i]
        if(current instanceof MyPromise){
          / / MyPromise object
         
        }else {
          // Non-myPromise object}}})}Copy the code

4. Store the results

As mentioned above, we need to store the data according to the situation, so we need to store the results in the corresponding order. Fortunately, the array can store the values through the key

static all (array) {
    // Store the result
    let result = [];
    function addData(key, value) {
      // Add values in the order they are executed.
      result[key] = value
    }
    return new MyPromise((resolve, reject) = >{
      // Why not use the for loop
      for(const i in array) {
        let current = array[i]
        if(current instanceof MyPromise){
          / / MyPromise object
          current.then(value= > addData(i, value), reason= > reject(reason))
        }else {
          // Non-myPromise object
          addData(i, current)
        }
      }
      resolve(result)
    })
  }
Copy the code

But there’s a problem with that! What’s the problem?

It doesn’t support asynchronous operations! Oh, my God! Oh, my god! Too the heart

Why not support asynchronous operations?

Don’t ask, ask is no! Just kidding, the for operation is synchronous, so the asynchronous operation will not execute in time, simple as that

Perfect the code!

We simply add a variable of how many times it has been executed, and then determine based on that variable. If the number of times it has been executed matches the length of our input parameter, we just tell it to execute resolve

static all (array) {
    // Store the result
    let result = [];
    let index = [];
    return new MyPromise((resolve, reject) = >{
      function addData(key, value) {
        // Add values in the order they are executed.
        result[key] = value
        index++;
        if(index === array.length){
          resolve(result)
        }
      }
      // Why not use the for loop
      for(const i in array) {
        let current = array[i]
        if(current instanceof MyPromise){
          / / MyPromise object
          current.then(value= > addData(i, value), reason= > reject(reason))
        }else {
          // Non-myPromise object
          addData(i, current)
        }
      }
    })
  }
Copy the code

test

function promise () {
  return new MyPromise((resolve, reject) = > {
    setTimeout(() = > {
      resolve('success')},2000)})}function promise1 () {
  return new MyPromise((resolve, reject) = > {
    resolve('1' success)
  })
}
MyPromise.all(['a'.'b',promise(),promise1()]).then((res) = > {
  console.log(res)
})
Copy the code

Expected output: [‘a’, ‘b’, ‘success ‘,’ success 1′]

The results of

Implementation of the promise.resolve method

The promise.resolve (value) method returns a Promise object resolved with the given value. If the value is a Promise, the promise is returned; If the value is thenable (that is, with the “then” method), the returned promise “follows “the thenable object and adopts its final state; Otherwise the returned promise will be fulfilled with this value. This function flattens out the multiple layers of nesting of promise-like objects.

How do you do that?

Resolve (value) will determine if the value is a Promise object. If it is, it will return the Promise object. If it is not, we need to wrap a Promise object.

In the code

static resolve (value) {
    if(value instanceof MyPromise){
      / / MyPromise object
      return value
    }else {
      // Non-myPromise object
      return new MyPromise(resolve= > resolve(value))
    }
  }
Copy the code

test

The test case

function promise () {
  return new MyPromise((resolve, reject) = > {
    setTimeout(() = > {
      resolve('success')},2000)})}function promise1 () {
  return new MyPromise((resolve, reject) = > {
    resolve('1' success)
  })
}
MyPromise.resolve(100).then(res= > console.log(res))
MyPromise.resolve(promise()).then(res= > console.log(res))
MyPromise.resolve(promise1()).then(res= > console.log(res))
Copy the code

Expected output: 100 success 1 success

The test results

Completed!

finallyMethod implementation

The finally method has two characteristics

  1. No matterpromiseWhether the object status succeededfinallyThe callback function is executed
  2. finallyYou can call it chainedthenMethod to get the return value

How do you do that?

First we can make sure that finally is not a static method, so we go into the file

 finally(callback){}Copy the code

Then what should I do?? Implement it bit by bit, as required

No matterpromiseWhether the object status succeededfinallyThe callback function is executed

If we call finally, we can also call then inside the function.

Remember the two arguments to then? We pass in and call our method at the same time!

finally(callback){
    this.then(() = >{
      callback()
    },() = >{
      callback()
    })
  };
Copy the code

Perfect solution!

finallyYou can call it chainedthenMethod to get the return value

We’ve talked about this a couple of times, we can chain call then and that’s just returning a promise object, right? So if we think about it then it looks like we’re returning this object so why can’t we return this then?

finally(callback){
   return this.then(() = >{
      callback()
    },() = >{
      callback()
    })
  };
Copy the code

But that doesn’t seem right! What if the chain call does not get arguments? Don’t panic! A little in the transformation of some good!

finally(callback){
   return this.then(value= >{
      callback()
      return value
    },reason= >{
      callback()
      throw reason
    })
  };
Copy the code

A problem has arisen

What’s going on?

Look at this test code:

function promise () {
  return new MyPromise((resolve, reject) = > {
    setTimeout(() = > {
      resolve('promise success')},1000)})}function promise1 () {
  return new MyPromise((resolve, reject) = > {
    setTimeout(() = > {
      resolve('promise1 success')},1000)
  })
}
promise().finally(() = > {
  console.log('promise finally')
  return promise1()
}).then(value= > { 
    console.log(value) 
  },reason= > { 
    console.log(reason) 
})
Copy the code

The expectation is to execute the promise1 function first and then, but this is not done

What to do? (THE GIF cannot be captured)

It occurred to me that it would be nice if we could wait for this method to be executed later. Depending on whether our execution method is being processed asynchronously and converted to a Promise object, then we can make subsequent chain calls and execute our method.

The resolve(value) method returns a resolved Promise object with the given value!!!! Then we can use it to modify the code!

To test

This test is ok, I don’t know why my GIF can not be captured, you will see my description!

There is no problem with the test result, it was executed after 1s, there is no problem!

Implementation of the catch method

The catch method is a function designed to handle the failure of a Promise method, so that you don’t have to pass a failed callback to the then method

implementation

catch (failCallback) {
    return this.then(undefined, failCallback)
};
Copy the code

It’s that simple!

Test the

The test code

const MyPromise = require('./myPromise')
function promise () {
  return new Promise((resolve, reject) = > {
    setTimeout(() = > {
      resolve('promise success')},1000)})}function promise1 () {
  return new MyPromise((resolve, reject) = > {
    setTimeout(() = > {
      reject('promise1 failure')},1000)
  })
}
promise()
  .then(val= > console.log(val))
  .catch(res= > console.log(res))

promise1()
  .then(val= > console.log(val))
  .catch(res= > console.log(res))
Copy the code

The results of

summary

And that’s where our handwritten code ends

The complete code

const PENDING = "pending"; / / wait for
const FULFILLED = "fulfilled"; / / success
const REJECTED = "reject"; / / fail
const isFunction = (value) = > typeof value === "function";
class MyPromise {
  / / Promise
  status = PENDING;
  // Success value
  value = undefined;
  // Failed value
  reason = undefined;
  // Successful callback
  successCallback = [];
  // Failed callback
  failCallback = [];
  // Errors need to be caught in the actuator
  constructor (callback) {
    try {
      callback(this.resolve, this.reject);
    } catch (e) {
      this.reject(e);
    }
  }
  resolve = (value) = > {
    // If the state is not wait, prevent the program from executing down
    if (this.status ! == PENDING) {return;
    }
    // Change the status to success
    this.status = FULFILLED;
    // Save the successful value
    this.value = value;
    // Determine if the successful callback exists, and if so, call
    // this.successCallback && this.successCallback(this.value)
    while (this.successCallback.length) {
      // Call the Shift method and each time it executes, push the method out of the array stack and return the data back
      this.successCallback.shift()(); }}; reject =(value) = > {
    // If the state is not wait, prevent the program from executing down
    if (this.status ! == PENDING) {return;
    }
    // Change the status to failed
    this.status = REJECTED;
    // Save the failed value
    this.reason = value;
    // Determine if the failed callback exists, and if so, invoke
    // this.failCallback && this.failCallback(this.reason)
    while (this.failCallback.length) {
      // Call the Shift method and each time it executes, push the method out of the array stack and return the data back
      this.failCallback.shift()(); }}; then (successCallback, failCallback) { successCallback = isFunction(successCallback) ? successCallback :(value) = > value;
    failCallback = isFunction(failCallback)
      ? failCallback
      : (err) = > {
        throw err;
      };
    let thenPromise = new MyPromise((resolve, reject) = > {
      // If the status is successful
      if (this.status === FULFILLED) {
        setTimeout(() = > {
          try {
            let x = successCallback(this.value);
            // Determine if x is MyPromise
            // If yes, go straight
            resolvePromise(thenPromise, x, resolve, reject);
          } catch(e) { reject(e); }},0);
        // resolve(x)
      } else if (this.status === REJECTED) {
        setTimeout(() = > {
          try {
            let x = failCallback(this.reason);
            resolvePromise(thenPromise, x, resolve, reject);
          } catch(e) { reject(e); }},0);
      } else {
        // The waiting condition
        this.successCallback.push(() = > {
          setTimeout(() = > {
            try {
              let x = successCallback(this.value);
              resolvePromise(thenPromise, x, resolve, reject);
            } catch(e) { reject(e); }},0);
        });
        this.failCallback.push(() = > {
          setTimeout(() = > {
            try {
              let x = failCallback(this.reason);
              resolvePromise(thenPromise, x, resolve, reject);
            } catch(e) { reject(e); }},0); }); }});return thenPromise;
  };
  finally (callback) {
    return this.then(value= > {
      return MyPromise.resolve(callback()).then(() = > value)
    }, reason= > {
      return MyPromise.resolve(callback()).then(() = > { throw reason })
    })
  };
  catch (failCallback) {
    return this.then(undefined, failCallback)
  };
  static all (array) {
    // Store the result
    let result = [];
    let index = [];
    return new MyPromise((resolve, reject) = > {
      function addData (key, value) {
        // Add values in the order they are executed.
        result[key] = value
        index++;
        if (index === array.length) {
          resolve(result)
        }
      }
      // Why not use the for loop
      for (const i in array) {
        let current = array[i]
        if (current instanceof MyPromise) {
          / / MyPromise object
          current.then(value= > addData(i, value), reason= > reject(reason))
        } else {
          // Non-myPromise object
          addData(i, current)
        }
      }
    })
  };
  static resolve (value) {
    if (value instanceof MyPromise) {
      / / MyPromise object
      return value
    } else {
      // Non-myPromise object
      return new MyPromise(resolve= > resolve(value))
    }
  }
}
// Resolve whether the promise function is available
// Instead of calling reslove directly
// Call reslove or reject as appropriate
function resolvePromise (thenPromise, value, resolve, reject) {
  if (thenPromise === value) {
    return reject(
      new TypeError("Chaining cycle detected for promise #<MyPromise>")); }if (value instanceof MyPromise) {
    value.then(resolve, reject);
  } else{ resolve(value); }}module.exports = MyPromise;

Copy the code

See here, give it a thumbs up!