background

When JS conducts asynchronous operations, it often uses promise to obtain the return result of asynchronous operations. How is promise implemented inside?

This article provides an in-depth understanding of the promise implementation logic by writing A promise class that conforms to the Promise A+ specification.

What is the Promise A+ specification

The Promise A+ specification is an open source, developer-implemented standard promise specification that specifies what features A standard promise needs to implement.

Promise A+ Specification related terms:

  1. A promise is an object or function that contains the then methods compatible with the Promise specification,
  2. Thenable is an object or function that contains the THEN method.
  3. Value is any Javascript value. (including undefined, thenable, promise, etc.)
  4. Exception is the value thrown by the throw expression.
  5. Reason is a value that describes why a Promise was rejected.

The following functions need to be implemented:

  1. Promise states, including pending, depressing, and Rejected.
  2. Then method, which includes onFulfilled and onRejected.

Promises A+ specification

So what are the apis that browsers currently implement for Promises

There are basically two kinds:

1, Class methods

Promise.all, Promise.resolve, Promise.reject, Promise.race

2. Methods on instances

then/catch/finally

Begin to implement

We usually use promises, which go something like this:

let promise = new Promise(function(resolve, reject) {
  // What if the asynchronous operation succeeds
  setTimeout((a)= > {
    let isSuccess = true
    if (isSuccess) {
      resolve('Interface returned successfully')}else {
      reject('Interface return failed')}},1000)
})

promise.then(value= > {
  console.log('success', value)
}, error => {
  console.log('error', error)
})

// Logs are printed
// -> The interface returned successfully
Copy the code

So how do we implement a promise that does this?

How to fulfill a promise

As you can see, promise is a class that passes a function that takes resolve and reject. This function is called the Executor executor, and the executor contains both resolve and reject.

Concrete implementation:

// Define the three state constants for promise
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

function Promise(executor) {
  // Initialize the Promise state and value values
  let that = this
  that.status = PENDING
  that.value = undefined

  // Resolve sets the state to successful when executed
  function resolve(value) {
    // Only when the status is pending, the status will be postponed and the value of value will be updated
    if (that.status === PENDING) {
      that.status = FULFILLED
      that.value = value
    }
  }

  // reject Sets the state to fail when executing
  function reject(reason) {
    if (that.status === PENDING) {
      that.status = REJECTED
      that.value = reason
    }
  }

  executor(resolve, reject)
}

Promise.prototype.then = function(onFulfilled, onRejected) {
  let that = this
  // If the current state is completed, the completed callback is executed
  if (that.status === FULFILLED) {
    onFulfilled(that.value)
  }
  // If the current state is failed, the failed callback is executed
  if (that.status === REJECTED) {
    onRejected(that.value)
  }
}
Copy the code

A simple promise implementation is complete, containing an implementation of New Promise(Executor: (Resolve, Reject) => void) and supporting logic for then calls to get value or Reason.

However, the implementation does not yet support asynchronous Resolve or Reject states

Add a publish/subscriber pattern to promise to support asynchronous status updates

Promise. Then takes precedence over resolve or reject if the promise is delayed by, say, 1000ms. At this time, the status of the Promise is still pending, and the onFulfilled and onRejected methods in THEN will not be implemented

Therefore, we will use the release/subscriber mode to store the onFulfilled and onRejected listening methods in the promise. Then, and notify the corresponding listening methods when the promise state is resolve or Reject

Add publish/subscriber mode

// Define the three state constants for promise
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

function Promise(executor) {
  // Initialize the Promise state and value values
  let that = this
  that.status = PENDING
  that.value = undefined
  // Initialize the callback array subscribed to the resolve and rejecte methods
  that.onFulfilledCallbacks = []
  that.onRejectedCallbacks = []
  // Resolve sets the state to successful when executed
  function resolve(value) {
    // Only when the status is pending, the status will be postponed and the value of value will be updated
    if (that.status === PENDING) {
      that.status = FULFILLED
      that.value = value

      Execute the callback method subscribed to resolve
      that.onFulfilledCallbacks.forEach(onFulfilled= > onFulfilled(that.value))
    }
  }

  // reject Sets the state to fail when executing
  function reject(reason) {
    if (that.status === PENDING) {
      that.status = REJECTED
      that.value = reason
      Execute the subscribe reject callback method
      that.onRejectedCallbacks.forEach(onRejected= > onRejected(that.value))
    }
  }

  executor(resolve, reject)
}

Promise.prototype.then = function(onFulfilled, onRejected) {
  let that = this
  // If the current state is completed, the completed callback is executed
  if (that.status === FULFILLED) {
    onFulfilled(that.value)
  }
  // If the current state is failed, the failed callback is executed
  if (that.status === REJECTED) {
    onRejected(that.value)
  }
  // If the current state is still pending, add the callback method to the callback for the subscription state
  if (that.status === PENDING) {
    // Subscribe to the resolve callback
    that.onFulfilledCallbacks.push(onFulfilled)
    Subscribe reject's callback
    that.onRejectedCallbacks.push(onRejected)
  }
}
Copy the code

test

Okay, so now that we’ve implemented promise, it’s really powerful enough to write a simple test case, run it.

let promise1 = new Promise(function(resolve, reject) {
  resolve('promise1- Return success directly ')})let promise2 = new Promise(function(resolve, reject) {
  setTimeout((a)= > {
    reject('promise2- Asynchronous return successful ')},1000)})let promise3 = new Promise(function(resolve, reject) {
  resolve('promise3- Return success directly ')
  reject('promise3- Return failure directly ')
})

promise1.then((value) = > {
  console.log('PromisE1 - Perform ondepressing', value)
}, (reason) => {
  console.log('promise1- Execute onRejected', reason)
})

promise2.then((value) = > {
  console.log('Promise2 - Perform ondepressing', value)
}, (reason) => {
  console.log('promise2- Execute onRejected', reason)
})

promise3.then((value) = > {
  console.log('Promise3 - Perform ondepressing', value)
}, (reason) => {
  console.log('promise3- Execute onRejected', reason)
})

// Execution result
// -> perform onFulfilled promise1- Interface returns successfully
// -> perform onFulfilled promise3- Interface returns success
/ / ms after 1000
// -> promise2- Execute onRejected promise2- Asyncio returns successfully
Copy the code

The test results meet the requirements

If onFulfilled, the interface returns successfully. If onFulfilled, the interface returns successfully

Promise2 delays the reject. After 1000ms, onRejected is triggered. Promise2 indicates that the onRejected is implemented

The promise state will be fulfilled later. Therefore, the state will be invalid if the promise state is fulfilled later

conclusion

Currently our promise implements the following:

  1. Implements the status status, including pending, fulfilled, rejected three states
  2. OnFulfilled and onRejected can be passed into onFulfilled and onFulfilled through the then method
  3. Implements the Executor executor and its resolve and Reject methods
  4. The then method is implemented. When the status is gradually replaced with pity or Rejected, the promise value will be passed to the onFulfilled and onRejected in the THEN method
  5. OnFulfilled and onFulfilled when the status is asynchronously updated, notify the THEN method

This is A simple conform to promise A promise to realize + specification, but promise and many other features and apis, such as: promise. Resolve, promise. Reject, promise. All, I will update in A later article.

To be continued…