Introduction of the overall process

  • Introduction of the overall process
  • 1. Define the overall structure
  • Implement the Promise constructor
  • 3. Implement the THEN method
  • 3. Implement the catch method
  • 4. Realize the Promise. Resolve
  • 5. Realize the Promise. Reject
  • 6. Realize the Promise. All
  • 7. Realize the Promise. Race

The article will explain why this is done with examples, in as vulgar and understandable a way as I can. If you have any questions or suggestions, please comment below. Project source code has been in the making, convenient point a star bai: https://github.com/Sunny-lucking/howToBuildMyPromise

1. Define the overall structure

  1. Write the constructor first, exposing the Promise outward
/ *Customize the Promise function module: IIFE* /

(function (window) {
 / *Promise constructorExecutor: An executor function* /  function Promise(executor) {   }   // Expose the Promise to the outside  window.Promise = Promise }) () Copy the code
  1. Add the method on the Promise prototype object
 / *The THEN of the Promise prototype objectSpecify a success/failure callback functionReturn a new PROMISE object* /
 Promise.prototype.then = function(onResolved,onRejected){   }   / *Promise the catch of the prototype objectSpecifies a failed callback functionReturn a new PROMISE object* /  Promise.prototype.catch = function(onRejected){   } Copy the code
  1. Add the method on the Promise function object
/ *The Resovle method of the Promise function objectReturn a PROMISE object with the specified result* /
    Promise.resolve = function(value){
  }   / *The REJECT method of the Promise function objectReturn a promise object with the failure status specified by reason* /  Promise.reject = function(value){   }   / *The all method of the Promise function objectReturn a Promise object. The returned promise state succeeds only if all promises are successful* /  Promise.all = function(0value){   }   / *The RACE method of the Promise function objectReturn a Promise object whose state is determined by the first completed promise* /  Promise.race = function(value){   } Copy the code

You can see from the comments above. The result of execution of a method on either a Promise prototype object or a Promise function object returns a Promise object

Implement the Promise constructor

Let’s see how we use Promise

const promiseA = new Promise( (resolve,reject) = > {
    resolve(777);
});
Copy the code
  1. We pass in a function, and it executes immediately, not only that, but also resolve and reject. The constructor has resolve and reject methods. Therefore, we can initially achieve:
    / *Promise constructorExecutor: An executor function* /
    function Promise(executor) {
  function resovle() {   }  function reject() {   }   // Execute Executor synchronously immediately  executor(resovle,reject)  }  Copy the code
  1. Each promise has a state that can be pending or resolved, rejected. And the initial state is pending. Therefore, you need to add a status to represent the current promise state. And each promise has its own data.
function Promise(executor) {

        var self = self
The new code        self.status = 'pending' // Give the promise object a status attribute with an initial value of pending
  self.data = undefined // Give the Promise object a data to store the result   function resovle() {   }  function reject() {   }   // Execute Executor synchronously immediately  executor(resovle,reject)  } Copy the code

Moreover, when we use promises in this way,

// 例1
var promise = new Promise((resovle,reject) = >{
    
})

promise.then(resolve= >{},reject=>{}) Copy the code

In this case, we need an array of Callbacks to save the callbacks in the then, so the promise is still pending because the immediate function we passed in did not execute resolve or reject

function Promise(executor) {

        var self = self

        self.status = 'pending' // Give the promise object a status attribute with an initial value of pending
 self.data = undefined // Give the Promise object a data to store the result The new code self.callbacks = [] {onResolved(){}, onRejected(){}}    function resovle() {   }  function reject() {   }   // Execute Executor synchronously immediately  executor(resovle,reject)  } Copy the code

How does the then function collect incoming callbacks? If the promise is pending, push the callback to the callbacks.

Promise.prototype.then = function(onResolved,onRejected){

        var self = this
        
        if(self.status === 'pending') { // Promise the current state or pending state, save the callback function  self.callbacks.push({  onResolved(){onResolved(self.data)},  onRejected(){onRejected(self.data)}  })  }else if(self.status === 'resolved') { }else{  }   } Copy the code
  1. Based on example 1 above, when we perform resovle (value), as in Example 2
// 例2
var promise = new Promise((resolve,reject) = >{
    setTimeout(function () {
        resolve(1)
    })
})  promise.then(  value= >{console.log(value)},  err=>{console.log(err)}  )  Copy the code

How does the code execute at this point?

  1. The new Promise thread executes the new Promise code, and then finds a timer. The JS thread assigns the timer to the timer thread. 2. Then continue to execute the following code and find that it is THEN and that the current promise is still pending. Just put the callback function from the then into the Callbacks.
  2. After 5 seconds, the timer thread puts the callback function (i.e., macro task) in the message queue. The JS thread finds the macro task in the message queue and executes it.
  3. Resolve (1) is executed, and the callback in the Promise’s callbacks is executed. Change the promise state to Resolved. The 1 is then saved to the current promise object

So how do you make resolve? Resovle executes functions in Callbacks, saves data, and changes the promise state to resolved. So we can do it this way

function resolve(value) {
    // Change the state to Resolved
    self.status = 'resolved'
    // Save the value of value
    self.data = value
  // If the callback function is waiting to be executed, immediately execute the onResolved callback asynchronously  if (self.callbacks.length>0) { self.callbacks.forEach(callbackObj= >{  callbackObj.onResolved(value)  })  } } Copy the code
  1. We also know that the state of a PROMISE can only change once, so when we execute resolve, we must determine whether the promise state is pending or not. Otherwise, we cannot execute
function resolve(value) {
    // If the current state is not pending, no execution is performed
    if(this.status ! = ='pending') {        return 
    }
 // Change the state to Resolved  this.status = 'resolved'  // Save the value of value  this.data = value   // If the callback function is waiting to be executed, immediately execute the onResolved callback asynchronously  if (this.callbacks.length>0) { setTimeout((a)= >{  this.callbacks.forEach(callbackObj= >{ A  callbackObj.onResolved(value)  })  })  } } Copy the code
  1. The nice thing about this is that it’s the same with reject, so there’s no need to go into it here
function reject(value) {
    // If the current state is not pending, no execution is performed
    if(self.status ! = ='pending') {        return
    }
 // Change the state to rejected  self.status = 'rejected'  // Save the value of value  self.data = value   // If the callback function is waiting to be executed, immediately execute the onResolved callback asynchronously  if (self.callbacks.length>0) { self.callbacks.forEach(callbackObj= >{  callbackObj.onRejected(value)  })  } } Copy the code
  1. We also know that when executing an Executor, the promise state executes the Reject method directly if an exception is made. For example,
/ / 3
var promise = new Promise((resolve,reject) = >{

The error; There was an error at this point
 setTimeout(function () {  resolve(1)  }) }) Copy the code

To do this, we can use a try catch outside of the Executor

try{
    // Execute Executor synchronously immediately
    executor(resolve,reject)
}catch (e) { // If the actuator throws an exception, the Promise object becomes the rejected state
    reject(e)
} Copy the code

All right, let’s test it out

 // 例4
 let promise = new Promise((resolve,reject) = >{
        
        setTimeout(function () {
            resolve(1)
 //reject(1)  },100)  })   promise.then(  value= >{  console.log("onResolved:",value);  },  reason=>{  console.log("onRejected:",reason);  }  )  Copy the code

Find success. OnResolved :1 is displayed successfully

3. Implement the THEN method

We simply implement the situation where the promise is pending, such as:

Promise.prototype.then = function(onResolved,onRejected){

    var self = this

    if(self.status === 'pending') { // Promise the current state or pending state, save the callback function  self.callbacks.push({  onResolved(){onResolved(self.data)},  onRejected(){onRejected(self.data)}  })  }else if(self.status === 'resolved') { }else{  }  } Copy the code

What about the other cases?

When the promise is in the ‘THEN’ state, the callback function in the ‘then’ state can be saved. When the promise is in the ‘THEN’ state, the callback function can be saved. When the promise is in the ‘THEN’ state, the callback function can be saved. Note that the execution is asynchronous. And as a microtask, here we simply use setTimeout to implement it.

Promise.prototype.then = function(onResolved,onRejected){

  var self = this

  if(self.status === 'pending') { // Promise the current state or pending state, save the callback function  self.callbacks.push({  onResolved(){onResolved(self.data)},  onRejected(){onRejected(self.data)}  })  }else if(self.status === 'resolved') { setTimeout((a)= >{  onResolved(self.data)  })  }else{  setTimeout((a)= >{  onResolved(self.data)  })  }  }  Copy the code

We also know that a new promise will be returned after the then, and that the state of the new promise will be determined by the result of the current THEN.

 Promise.prototype.then = function(onResolved,onRejected){

    var self = this

    return new Promise((resolve,reject) = >{
 if(self.status === 'pending') { // Promise the current state or pending state, save the callback function  self.callbacks.push({  onResolved(){onResolved(self.data)},  onRejected(){onRejected(self.data)}  })  }else if(self.status === 'resolved') { setTimeout((a)= >{  onResolved(self.data)  })  }else{  setTimeout((a)= >{  onResolved(self.data)  })  }  })  } Copy the code

When the promise is in the Resolved state, the second determination statement is executed when the THEN statement is executed

Then three situations will occur when the second judgment statement is currently executed

  1. If the callback in then does not return a promise, the new promise that returns is in the resolved state and value is the value returned. Such as:
/ / case 5
let promise = new Promise((resolve,reject) = >{
    resolve(1)
})

promise.then(  value= >{  return value // Return a value instead of a promise  } ) Copy the code

So, we can do this

Promise.prototype.then = function(onResolved,onRejected){

    var self = this

    return new Promise((resolve,reject) = >{
 if(self.status === 'pending') { // Promise the current state or pending state, save the callback function  self.callbacks.push({  onResolved(){onResolved(self.data)},  onRejected(){onRejected(self.data)}  })  }else if(self.status === 'resolved') {Modify the code setTimeout((a)= >{  const result = onResolved(self.data)  if (result instanceof Promise) {  } else {  // 1. If the return promise is in the resolved state and is not a promise, value is the value returned.  resolve(result)  }  })  }else{  setTimeout((a)= >{  onResolved(self.data)  })  }  })  } Copy the code

A quick explanation:

The onResolved(self.data) callback function is executed as shown in the example below

value=>{
        return value // Return a value instead of a promise
    }
Copy the code

So this callback function returns value. The resolve function changes the state of the current promise to resolved and saves the value in the data of the current promise.

  1. If the callback returns a promise, the result of the return promise is the result of that promise, and as the code shows, we return a new promise. If the promise executes resolve, the new promise that is returned is in resolved state. Otherwise it is rejected
/ / case 6
let promise = new Promise((resolve,reject) = >{
    resolve(1)
})

promise.then(  value= >{  return new Promise((resolve,reject) = >{  resolve(2)  / / or  //reject(error)  })  } ) Copy the code

So we can do this

Promise.prototype.then = function(onResolved,onRejected){

    var self = this

    return new Promise((resolve,reject) = >{
 if(self.status === 'pending') { // Promise the current state or pending state, save the callback function  self.callbacks.push({  onResolved(){onResolved(self.data)},  onRejected(){onRejected(self.data)}  })  }else if(self.status === 'resolved') { setTimeout((a)= >{  const result = onResolved(self.data)  if (result instanceof Promise) { // 2. If the callback function returns a promise, the result of the return promise is the result of that promise  result.then(  value= > {resolve(value)},  reason => {reject(reason)}  )  } else {  // 1. If the return promise is in the resolved state and is not a promise, value is the value returned.  resolve(result)  }  })  }else{  setTimeout((a)= >{  onResolved(self.data)  })  }  })  }  Copy the code

Here’s the explanation:

result.then(
    value => {resolve(value)},
    reason => {reject(reason)}
)
Copy the code

Since we executed the then in Example 6

value=>{
        return new Promise((resolve,reject) = >{
            resolve(2)
            / / or
            //reject(error)
 })  } Copy the code

Return a Promise object that can be in the Resolved state (resolve(2)) or rejected (Reject (Error)).

This causes value => {resolve(value)}, the callback function to be executed, or reason => {reject(Reason)} to be executed.

Therefore, it sets the data of the promise to be returned to value or reason. It will set the status to Resolved or Rejected.

  1. If this code throws an error and the promise is rejected, we can use try catch to implement it
setTimeout((a)= >{
    try{
        const result = onResolved(self.data)
        if (result instanceof Promise) {            // 2. If the callback function returns a promise, the result of the return promise is the result of that promise
 result.then(  value= > {resolve(value)},  reason => {reject(reason)}  )  } else {  // 1. If the return promise is in the resolved state and is not a promise, value is the value returned.  resolve(result)  }  }catch (e) {  // 3. If an error is thrown when onResolved is executed, promise is returned with the status rejected  reject(e)  } }) Copy the code

Status === ‘rejected’

 setTimeout((a)= >{
      try{
          const result = onRejected(self.data)
          if (result instanceof Promise) {              // 2. If the callback function returns a promise, the result of the return promise is the result of that promise
 result.then(  value= > {resolve(value)},  reason => {reject(reason)}  )  } else {  // 1. If the return promise is in the resolved state and is not a promise, value is the value returned.  resolve(result)  }  }catch (e) {  // 3. If an error is thrown when onResolved is executed, promise is returned with the status rejected  reject(e)  }  }) Copy the code

Resolve: onResolved(self. Data) and onRejected(self. Data

 self.callbacks.push({
    onResolved(){onResolved(self.data)},
    onRejected(){onRejected(self.data)}
})
Copy the code

to

if(self.status === 'pending') {// Promise the current state or pending state, save the callback function
self.callbacks.push({
    onResolved(){
        try{
 const result = onResolved(self.data)  if (result instanceof Promise) { // 2. If the callback function returns a promise, the result of the return promise is the result of that promise  result.then(  value= > {resolve(value)},  reason => {reject(reason)}  )  } else {  // 1. If the return promise is in the resolved state and is not a promise, value is the value returned.  resolve(result)  }  }catch (e) {  // 3. If an error is thrown when onResolved is executed, promise is returned with the status rejected  reject(e)  }  }, Copy the code

At this point, we find that there is so much of the same code that it is necessary to encapsulate it

 function handle(callback) {
    try{
        const result = callback(self.data)
        if (result instanceof Promise){
// 2. If the callback returns a promise,returnThe result of the promise is the result of the promise result.then(  value => {resolve(value)},  reason => {reject(reason)}  )  } else { // 1. If the callback does not return a promise,returnThe state of promise is Resolved and value is the returned value. resolve(result)  }  }catch (e) { // 3. If an error is thrown when onResolved is executed, promise is returned with the status rejected reject(e)  } } Copy the code

So since relaxed a lot of

   Promise.prototype.then = function(onResolved,onRejected){

        var self = this

        return new Promise((resolve,reject) = >{
 / *Calls the processing of the specified callback function, based on the result of execution. Change the promise state of return* /  function handle(callback) {  try{  const result = callback(self.data)  if (result instanceof Promise) { // 2. If the callback function returns a promise, the result of the return promise is the result of that promise  result.then(  value= > {resolve(value)},  reason => {reject(reason)}  )  } else {  // 1. If the return promise is in the resolved state and is not a promise, value is the value returned.  resolve(result)  }  }catch (e) {  // 3. If an error is thrown when onResolved is executed, promise is returned with the status rejected  reject(e)  }  }  if(self.status === 'pending') { // Promise the current state or pending state, save the callback function  self.callbacks.push({  onResolved(){  handle(onResolved)  },  onRejected(){  handle(onRejected)  }  })  }else if(self.status === 'resolved') { setTimeout((a)= >{  handle(onResolved)  })  }else{ // when status === 'rejected'  setTimeout((a)= >{  handle(onRejected)  })  }  })   }  Copy the code

In addition, we also know that a promise will occur value passthrough, for example


let promsie = new Promise((resolve,reject) = >{
    resolve(1)
})
promsie
 .then(2)  .then(3)  .then(value= >console.log(value)) Copy the code

Running result: 1

If a.then or.catch argument is expected to be a function, passing a non-function will cause value penetration. Value passthrough means that the THEN is invalid if the then is passed to a function other than a function. In practice, when a function is not passed in the then, the promise returned by the then will hold the previous promise.data. This is where value penetration occurs. And every promise returned from an invalid THEN is in the resolved state.

Therefore, to implement the direct transmission through feature, we can do this

Add these sentences to determine whether value passthrough should occur

onResolved = typeof onResolved === 'function'? onResolved: value= > value
onRejected = typeof onRejected === 'function'? onRejected: reason= > {throw reason}
Copy the code

It’s essentially rewriting, if it’s not a function, ignore that value and write another function. The result of this function is to return the data of the previous promise

Promise.prototype.then = function(onResolved,onRejected){
    onResolved = typeof onResolved === 'function'? onResolved: value= > value
    onRejected = typeof onRejected === 'function'? onRejected: reason= > {throw reason}
    var self = this

 return new Promise((resolve,reject) = >{   / *Calls the processing of the specified callback function, based on the result of execution. Change the promise state of return* /  function handle(callback) {  try{  const result = callback(self.data)  if (result instanceof Promise) { // 2. If the callback function returns a promise, the result of the return promise is the result of that promise  result.then(  value= > {resolve(value)},  reason => {reject(reason)}  )  } else {  // 1. If the return promise is in the resolved state and is not a promise, value is the value returned.  resolve(result)  }  }catch (e) {  // 3. If an error is thrown when onResolved is executed, promise is returned with the status rejected  reject(e)  }  }  if(self.status === 'pending') { // Promise the current state or pending state, save the callback function  self.callbacks.push({  onResolved(){  handle(onResolved)  },  onRejected(){  handle(onRejected)  }  })  }else if(self.status === 'resolved') { setTimeout((a)= >{  handle(onResolved)  })  }else{ // when status === 'rejected'  setTimeout((a)= >{  handle(onRejected)  })  }  })  }  Copy the code

3. Implement the catch method

The catch function is the same as the second song callback function in then, so we can do this

Promise.prototype.catch = function(onRejected){
    return this.then(undefined,onRejected)
}
Copy the code

Oh, my god. It’s so easy

4. Realize the Promise. Resolve

As we all know, the promise. resolve method can pass three values

  1. Not a promise
  2. The promise of success
  3. Failure state promise

    Promise.resolve(1)
    Promise.resolve(Promise.resolve(1))
    Promise.resolve(Promise.reject(1))
Copy the code

It’s actually a little bit like implementing the THEN above

Promise.resolve = function(value){
  return new Promise((resolve,reject) = >{
      if (value instanceof Promise) {          // If the value is a promise
          value.then(
 value= > {resolve(value)},  reason => {reject(reason)}  )  } else{  // If the value is not a promise  resolve(value)  }   }  } Copy the code

5. Realize the Promise. Reject

Implementing this is easy, just return a promise with the status Rejected

/ *The REJECT method of the Promise function objectReturn a promise object with the failure status specified by reason* /
Promise.reject = function(reason){
 return new Promise((resolve,reject) = >{  reject(reason)  }) }  Copy the code

6. Realize the Promise. All

As we know, this method returns a promise

    / *The all method of the Promise function objectReturn a Promise object. The returned promise state succeeds only if all promises are successful* /
    Promise.all = function(promises){
 return new Promise((resolve,reject) = >{   })  }  Copy the code

The state of the promise is determined by the result of traversing each promise

    / *The all method of the Promise function objectReturn a Promise object. The returned promise state succeeds only if all promises are successful* /
    Promise.all = function(promises){
 return new Promise((resolve,reject) = >{  // Iterate over promises to get the result of each promise  promises.forEach((p,index) = >{   })  })  } Copy the code

There are two outcomes:

  1. If a promise is rejected, the promise state is rejected
 Promise.all = function(promises){
        return new Promise((resolve,reject) = >{
            // Iterate over promises to get the result of each promise
            promises.forEach((p,index) = >{
                p.then(
 value= > {   },  reason => { // As long as there is a failure, the return promise state is reject  reject(reason)  }  )  })  })  } Copy the code
  1. If all the promises traversed are in the Resolved state, the returned promise is in the Resolved state and the resulting value for each promise is passed on
   Promise.all = function(promises){
      const values = new Array(promises.length)
      var resolvedCount = 0 // Count the number of promises in the resolved state
      return new Promise((resolve,reject) = >{
          // Iterate over promises to get the result of each promise
 promises.forEach((p,index) = >{  p.then(  value= > {  // the state of p is resolved and the value is saved  values[index] = value  resolvedCount++;  // If all ps are resolved, the return promise state is resolved  if(resolvedCount === promises.length){  resolve(values)  }  },  reason => { // As long as there is a failure, the return promise state is reject  reject(reason)  }  )  })  })  } Copy the code

It looks like that, but there’s still a problem here, which is that the array all passes in doesn’t have to be all Promise objects, so maybe it is

All (a/p, 2, 3, p)Copy the code

So you need to wrap a number that isn’t a Promise as a promise

    Promise.all = function(promises){
        const values = new Array(promises.length)
        var resolvedCount = 0 // Count the number of promises in the resolved state
        return new Promise((resolve,reject) = >{
            // Iterate over promises to get the result of each promise
 promises.forEach((p,index) = >{  Promise.resolve(p).then(  value= > {  // the state of p is resolved and the value is saved  values[index] = value  resolvedCount++;  // If all ps are resolved, the return promise state is resolved  if(resolvedCount === promises.length){  resolve(values)  }  },  reason => { // As long as there is a failure, the return promise state is reject  reject(reason)  }  )  })  })  } Copy the code

7. Realize the Promise. Race

This method is much simpler to implement than all

  / *The RACE method of the Promise function objectReturn a Promise object whose state is determined by the first completed promise* /
    Promise.race = function(promises){
 return new Promise((resolve,reject) = >{  // Iterate over promises to get the result of each promise  promises.forEach((p,index) = >{  Promise.resolve(p).then(  value= > {  // As long as there is a success, return the promise of the state nine tail resolved  resolve(value)   },  reason => { // As long as there is a failure, the return promise state is reject  reject(reason)  }  )  })  })  }  Copy the code

If you have any questions or suggestions, please comment below. Project source code already in github:https://github.com/Sunny-lucking/howToBuildMyPromise conveniently point a star

So let’s do it, let’s see if we’ve got it? Do 45 Promise interview questions at a time (1.1w)

There may be some problems that you can’t do before, but when you finish reading this article, you can do it again, and find it is as easy as a palm.

This article was typeset using MDNICE