Implementing promises should be accomplished on the basis of skillful use. First, there are a few things to be clear about

  1. Promise is a class
  2. Promise has three states:This is a big pity, which is my success and my failure, which must be postponed.
    1. pending -> fulfilled / rejected
    2. Once a state is established, it cannot be changed
  3. The resolve and reject functions are used to change state
    1. resolve: fulfilled
    2. reject: rejected
  4. Then is called with two parameters. The first parameter is the callback function when the state is fulfilled, and the second parameter is the callback function when the state is Rejected

First edition – Implementation of basic core principles

const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(exector) {
    exector(this.reslove, this.reject)
  }
  
  status = PENDING / / promise
  value = undefined // The value on success then method needs to return
  error = undefined // The value of the failed then method needs to be returned
  
  // The function on success
  reslove = value= > {
    // Change the promise state
    this.status = FULFILLED
    this.value = value
  }
  // Function on failure
  reject = error= > {
    // Change the promise state
    this.status = REJECTED
    this.error = error
  }
  
  then(successCallback, failCallback) {
    switch (this.status) {
            case FULFILLED:
                successCallback(this.value)
                break
            case REJECTED:
                failCallback(this.error)
                break}}}// test
const promise = new MyPromise((reslove, reject) = > {
    reslove('reslove')
})
  
promise.then(value= > {
    console.log(value) // reslove
}, error= > {
    console.log(error)
})
Copy the code

With the above code, we successfully implemented the promise fundamentals. Calling the promise.then method successfully prints’ reslove ‘. However, we do not deal with PENDING state in the implementation of the THEN method. That is, if we do an asynchronous operation, the current promise cannot be processed. Such as:

const promise = new MyPromise((reslove, reject) = > {
  setTimeout(() = > {
    reslove('reslove')},2000)})Copy the code

Version 2 – Handles asynchronous cases

What about asynchrony? Using the setTimeout example above, reslove is executed after 2 seconds, and the state of the current Promise is still PENDING within 2 seconds of calling the THEN method. Two-step solution:

  1. PENDING state, success or failure callback is stored temporarily
  2. Reslove/Reject determines whether there is a callback and executes it if there is
const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(exector) {
    exector(this.reslove, this.reject)
  }
  
  status = PENDING / / promise
  value = undefined // The value on success then method needs to return
  error = undefined // The value of the failed then method needs to be returned
    successCallback = undefined // Successful callback
    failCallback = undefined // Failed callback
  // The function on success
  reslove = value= > {
    // Change the promise state
    this.status = FULFILLED
    this.value = value
    this.successCallback && this.successCallback(this.value)
  }
  // Function on failure
  reject = error= > {
    // Change the promise state
    this.status = REJECTED
    this.error = error
    this.failCallback && this.failCallback(this.error)
  }
  then(successCallback, failCallback) {
    switch (this.status) {
            case FULFILLED:
                successCallback(this.value)
                break
            case REJECTED:
                failCallBack(this.error)
                break
      // Asynchronous case processing
      case PENDING:
        this.successCallback = successCallback
        this.failCallback = failCallback
                break}}}// test
const promise = new MyPromise((reslove, reject) = > {
    setTimeout(() = > {
        reslove('reslove')},2000)    
})
  
promise.then(value= > {
    console.log(value) // after 2 seconds: reslove
}, error= > {
    console.log(error)
})
Copy the code

Once the second version was implemented, the problem arose again. When we normally use a promise, the then method under the same promise can be called multiple times. Let’s change the test code to

// test
const promise = new MyPromise((reslove, reject) = > {
  setTimeout(() = > {
    reslove('reslove')},2000)  
})
promise.then(value= > {
    console.log('First call then:', value)
})
promise.then(value= > {
    console.log('Second call then:', value)
})
// Call then reslove the second time
Copy the code

Finding that the code only executed the last THEN method, print “second call THEN: reslove”. This is obviously not in line with our expectations

Version 3 – Multiple calls

To implement multiple calls, all we need to do is hold multiple callbacks as an array and call them in turn with reslove/ Reject

const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
  constructor(exector) {
    exector(this.reslove, this.reject)
  }
  status = PENDING / / promise
  value = undefined // The value on success then method needs to return
  error = undefined // The value of the failed then method needs to be returned
  // Array ephemeral
  successCallback = [] // Successful callback
  failCallback = [] // Failed callback
  // The function on success
  reslove = value= > {
    // Change the promise state
    this.status = FULFILLED
    this.value = value
    // call in sequence
    while(this.successCallback.length) this.successCallback.shift()(this.value)
  }
  // Function on failure
  reject = error= > {
    // Change the promise state
    this.status = REJECTED
    this.error = error
    // call in sequence
    while(this.failCallback.length) this.failCallback.shift()(this.error)
  }
  then(successCallback, failCallback) {
    switch (this.status) {
      case FULFILLED:
        successCallback(this.value)
        break
      case REJECTED:
        failCallBack(this.error)
        break
        // Asynchronous case processing
      case PENDING:
        this.successCallback.push(successCallback)
        this.failCallback.push(failCallback)
        break}}}// test
const promise = new MyPromise((reslove, reject) = > {
  setTimeout(() = > {
    reslove('reslove')},2000)  
})
promise.then(value= > {
  console.log('First call then:', value)
})
promise.then(value= > {
  console.log('Second call then:', value)
})
promise.then(value= > {
  console.log('Third call then:', value)
})
// First call then: reslove
// Then: reslove
// Then: reslove
Copy the code

In addition to multiple calls, Promise also supports chained calls. The value returned by the then callback is the same as the value returned by the previous THEN callback.

Fourth edition – Chain calls

The important thing to note here is that we need to determine what type the return value of the previous THEN was. If it is a normal value, you can call the reslove method directly. In the case of a Promise object, you need to call reslove or Reject depending on what the Promise object returns.

const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
  constructor(exector) {
    exector(this.reslove, this.reject)
  }
  status = PENDING / / promise
  value = undefined // The value on success then method needs to return
  error = undefined // The value of the failed then method needs to be returned
  successCallback = [] // Successful callback
  failCallback = [] // Failed callback
  // The function on success
  reslove = value= > {
    // Change the promise state
    this.status = FULFILLED
    this.value = value
    while(this.successCallback.length) this.successCallback.shift()(this.value)
  }
  // Function on failure
  reject = error= > {
    // Change the promise state
    this.status = REJECTED
    this.error = error
    while(this.failCallback.length) this.failCallback.shift()(this.error)
  }
  then(successCallback, failCallback) {
    return new Promise((reslove, reject) = > {
      switch (this.status) {
        case FULFILLED:
          reslovePromise(successCallback(this.value), reslove, reject)  
          break
        case REJECTED:
          failCallBack(this.error)
          break
          // Asynchronous case processing
        case PENDING:
          this.successCallback.push(successCallback)
          this.failCallback.push(failCallback)
          break}}}})// This is a big pity. // This is a big pity. // This is a big pity.
function reslovePromise(x, reslove, reject) {
  if (x instanceof MyPromise) {
    x.then(reslove, reject)
  } else {
    reslove(x)
  }
}
// test
const promise = new MyPromise((reslove, reject) = > {
  reslove('reslove')    
})
promise.then(value= > {
  console.log('First call then:', value)
  return 'reslove2'
}).then(value= > {
  console.log('Second call then:', value)
  return new MyPromise((reslove, reject) = > {
    reslove('reslove3')
  })
}).then(value= > {
  console.log('Third call then:', value)
})
// First call then: reslove
// Second call then: reslove2
// Then: reslove3
Copy the code

By version 4, our MyPromise implemented the basic functionality of promises. But as you can see from the code, we didn’t do any error handling for MyPromise, which is not what robust code looks like. So let’s do some code optimization and error handling for MyPromise.

Fifth edition – Code optimization and error handling

Error handling has two things in mind. Reject (reject); reject (reject); reject (reject); The second is to recognize that the promise returns from the object.

const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(exector) {
        try {
            exector(this.reslove, this.reject)
        } catch(e) {
            this.reject(e)
        }
    }

    status = PENDING / / promise
    value = undefined // The value on success then method needs to return
    error = undefined // The value of the failed then method needs to be returned
    successCallback = [] // Successful callback
    failCallback = [] // Failed callback
    // The function on success
    reslove = value= > {
        // Change the promise state
        this.status = FULFILLED
        this.value = value
        while(this.successCallback.length) this.successCallback.shift()()
    }
    // Function on failure
    reject = error= > {
        // Change the promise state
        this.status = REJECTED
        this.error = error
        while(this.failCallback.length) this.failCallback.shift()()
    }
    then(successCallback, failCallback) {
        // Make the arguments of the then method optional
        successCallback = successCallback ? successCallback : value= > value
        failCallback = failCallback ? failCallback : error= > {throw error}
        let newPromise = new MyPromise((reslove, reject) = > {
            switch (this.status) {
                case FULFILLED:
                    setTimeout(() = > {
                        try {
                            reslovePromise(newPromise, successCallback(this.value), reslove, reject)
                        } catch(e) {
                            reject(e)
                        }       
                    }, 0)               
                    break
                case REJECTED:
                setTimeout(() = > {
                        try {
                            reslovePromise(newPromise, failCallback(this.error), reslove, reject)
                        } catch(e) {
                            reject(e)
                        }       
                    }, 0)
                    break
                // Asynchronous case processing
                case PENDING:
                    this.successCallback.push(() = > {
                        setTimeout(() = > {
                            try {
                                reslovePromise(newPromise, successCallback(this.value), reslove, reject)
                            } catch(e) {
                                reject(e)
                            }       
                        }, 0)})this.failCallback.push(() = > {
                        setTimeout(() = > {
                            try {
                                reslovePromise(newPromise, failCallback(this.error), reslove, reject)
                            } catch(e) {
                                reject(e)
                            }       
                        }, 0)})break}})return newPromise   
    }
}
// This is a big pity. This is a big pity. // This is a big pity.
function reslovePromise(newPromise ,x, reslove, reject) {
    if (newPromise === x) {
        return reject(new TypeError('Loop call'))}if (x instanceof MyPromise) {
        x.then(reslove, reject)
    } else {
        reslove(x)
    }
}
// test
const promise = new MyPromise((reslove, reject) = > {
    setTimeout(() = > {
        reslove('reslove')},2000)

    // reject('reject')
})
promise.then(value= > {
    console.log('First call then reslove:', value)
    throw new Error('then error')},error= > {
    console.log('First call then Reject:', error)
}).then(value= > {
    console.log('Then reslove:', value)
}, error= > {
    console.log('Second call then Reject:', error)
})
// Then reslove: reslove
// Then reject: Error: then Error
Copy the code