Simple Implementation of Promises/A+Specification

Keywords: Javascript

Author: @gauseen

The Promises/A + specification can be Here See

promise has three states, pending, fulfilled and rejected

  • promise in pending state

    • You can switch to fulfilled or rejected state
  • promise in fulfilled state

    • Can't switch to other states
    • Must have a value that cannot be changed
  • promise in rejected state

    • Can't switch to other states
    • There must be a reason value that cannot be changed
// Three states of promise
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

// MyPromise constructor
function MyPromise (fn) {
  // Initialization state
  this.state = PENDING
  this.result = void 0
  this.handlerQueue = []

  let resolve = (value) => {
    transitionState(this, FULFILLED, value)
  }

  let reject = (reason) => {
    transitionState(this, REJECTED, reason)
  }

  // Call Promise constructor callback
  try {
    fn(resolve, reject)
  } catch (error) {
    reject(error)
  }
}

The state migration method, which calls resolution in FN (resolution, reject), needs to change the promise state:

pending --> fulfilled

pending --> rejected

function transitionState (promise, state, result) {
  if (promise.state !== PENDING) return
  promise.state = state
  promise.result = result

  // Here's a pit first.
}

then The method returns a new one Promise Example (Note: Not the original one) Promise Only in this way can we make continuous chain calls and change state in turn

MyPromise.prototype.then = function (onFulfilled, onRejected) {
  return new MyPromise((resolve, reject) => {
    let handler = { onFulfilled, onRejected, resolve, reject }
    // If the current state is pending, place it in the handlerQueue queue and wait for the resolve or reject method to change its state
    // Otherwise, call directly the resolve or reject callback function in the then method
    if (this.state ==== PENDING) {
      this.handlerQueue.push(handler)
    } else {
      dispatchHandler(handler, this.state, this.result)
    }
  })
}
const isFunction = arg => typeof arg === 'function'

function dispatchHandler (handler, state, result) {
  let { onFulfilled, onRejected, resolve, reject } = handler

  if (state === FULFILLED) {
    let finalValue = isFunction(onFulfilled) ? onFulfilled(result) : result
    resolve(finalValue)
  } else if (state === REJECTED) {
    let finalReason = isFunction(onRejected) ? onRejected(result) : result
    reject(finalReason)
  }
}

The above code only supports synchronous invocation of Promise callback function parameters resolve and reject, as follows:

// Support

let myPromise = new MyPromise((resolve, reject) => {
  // resolve synchronous call
  resolve('Synchronous invocation value')
})

myPromise.then((value) => {
  console.log('value: ', value)
}, (reason) => {
  console.log('reason: ', reason)
})

However, the use of asynchronous calls is not supported, as shown in the following example code:

// Temporary support

let myPromise = new MyPromise((resolve, reject) => {
  // resolve asynchronous call
  setTimeout(() => {
    resolve('Asynchronous call value')
  })
})

myPromise.then((value) => {
  console.log('value: ', value)
}, (reason) => {
  console.log('reason: ', reason)
})

Asynchronous call to resolve or reject is not supported because of the following code fragments in the then method:

  // When the solution is called asynchronously and the then method executes, the promise state is pending.
  // So the N callback functions onFulfilled and onRejected are not called in the handlerQueue queue
  if (this.state ==== PENDING) {
    this.handlerQueue.push(handler)
  } else {
    // ...
  }

To support asynchronous invocation of resolve and reject, the state migration method transitionState is modified as follows:

function transitionState (promise, state, result) {
  if (promise.state !== PENDING) return
  promise.state = state
  promise.result = result

  // New Code Start
  promise.handlerQueue.forEach(handler => {
    dispatchHandler(handler, state, result)
  })
  // End of new code
}

Because the catch method is an alias for. then(null, onRejected), the implementation of the catch code is as follows:

MyPromise.prototype.catch = function (onRejected) {
  return this.then(null, onRejected)
}

As mentioned above, promise is implemented simply to support chain call of then and catch

Welcome to Pay Attention to Non-Advertising Articles Public Number: Front-end

Reference resources

promise-aplus-impl

Posted by mmorton on Sat, 12 Oct 2019 09:11:07 -0700