7
头图

Promise is a commonly used grammar in development. Basically, most asynchronous processing is done through Promise . There are many Promise specifications. ES6 finally adopted the Promise/A+ specification, so the following code is basically written based on this specification.

First, we first list all the instance methods and static methods of Promise

instance method

  • then: new Promise((resolve, reject) => {...}).then(() => {console.log('rsolve success callback')}, () => {console.log('reject Failure callback')})
  • catch: new Promise((resolve, reject) => {...}).catch(() => {console.log('reject failed method')})
  • finally: new Promise((resolve, reject) => {...}).finally(() => {console.log('Success and failure are entered')})
  • The above method calls will all return the new Promise

static method

  • resolve: Promise.resolve(value) returns Promise instance of 060dc39a8307ab
  • reject: Promise.reject(value) returns the Promise instance
  • all: Promise.all(promises) array format Promise and return a new Promise instance. If it succeeds, it will return the values in order, and if one of them fails, it will directly become a failure.
  • Race: Promise.race(promises) : incoming array format Promise and returns the new Promise instance, success or failure depends on the completion of the first way

Promise status of 060dc39a830860 is determined to be changed, it cannot be changed. There are three statuses: pending , fulfilled , rejected
Promise in the browser is placed in the microtask queue, which needs to be processed ( JavaScript Event Loop (event loop) mechanism )

1. Declare the instance method of Promise

class Promise {
  _value
  _state = 'pending'
  _queue = []
  constructor(fn) {
    if (typeof fn !== 'function') {
      throw 'Promise resolver undefined is not a function'
    }
    /* 
      new Promise((resolve, reject) => {
        resolve: 成功
        reject: 失败
      })
    */
    fn(this._resolve.bind(this), this._reject.bind(this))
  }

  // 接收1-2参数,第一个为成功的回调,第二个为失败的回调
  then(onFulfilled, onRejected) {
    // 有可能已经resolve了,因为Promise可以提前resolve,然后then方法后面注册
    if (this._state === 'fulfilled') {
      onFulfilled?.(this._value)
      return
    }
    // reject同理
    if (this._state === 'rejected') {
      onRejected?.(this._value)
      return
    }
    // Promise还没有完成,push到一个队列,到时候完成的时候,执行这个队列里面对应的函数
    this._queue.push({
      onFulfilled,
      onRejected,
    })
  }

  // 接收失败的回调
  catch(onRejected) {
    // 相当于直接调用then传入失败的回调
    this.then(null, onRejected)
  }

  // 成功与失败都执行的回调
  finally(onDone) {
    const fn = () => onDone()
    this.then(fn, fn)
  }
  // 成功resolve
  _resolve(value) {
    // 状态确定了,就不再发生变化了
    if (this._state !== 'pending') return
    this._state = 'fulfilled'

    // 把值存起来,当再次调用的时候直接取这个值就行,因为Promise一旦确定就不会发生改变了
    this._value = value

    // 执行前面.then方法里面push函数形式的参数,这样就执行对应的方法了。
    this._queue.forEach((callback) => {
      callback.onFulfilled?.(this._value)
    })
  }

  // 失败reject
  _reject(error) {
    // 状态确定了,就不再发生变化了
    if (this._state !== 'pending') return
    this._state = 'rejected'
    this._value = error
    this._queue.forEach((callback) => {
      callback.onRejected?.(this._value)
    })
  }
}

Calling logic:

  1. Use the then method to pass in the parameters of the function form, that is, onFulfilled => then((onFulfilled, onRejected) => {...})
  2. In the then method, put the onFulfilled function into the set _queue => this._queue.push({ onFulfilled, onRejected })
  3. And other asynchronous callback, run resolve function, this time calling _queue collect good through then function registration method. Execute these functions uniformly, so that the asynchronous callback is completed, and the corresponding then method are executed
// 结果打印
const p = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  }, 1000)
})
p.then((res) => {
  console.log(res) // => success
})

// reject
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('fail')
  }, 1000)
})
p1.catch((res) => {
  console.log(res) // => fail
})

// finally
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve()
  }, 1000)
})
p2.finally(() => {
  console.log('done') // => done
})
online code demo

2. Micro task processing and return Promise

a. Perform micro-task processing

Promise completed in the browser, it will be pushed into the micro task, so we also need to do this processing. Use MutationObserver , node can use process.nextTick

class Promise {
  ...
  // 推入微任务
  _nextTick(fn) {
    if (typeof MutationObserver !== 'undefined') { // 浏览器通过MutationObserver实现微任务的效果
      // 这块可以单独拿出来共用,避免不必要的开销,不然每次都需要生成节点。
      const observer = new MutationObserver(fn)
      let count = 1
      const textNode = document.createTextNode(String(count))
      observer.observe(textNode, {
        characterData: true
      })
      textNode.data = String(++count)
    } else if (typeof process.nextTick !== 'undefined') { // node端通过process.nextTick来实现
      process.nextTick(fn)
    } else {
      setTimeout(fn, 0)
    }
  }
  // 成功resolve
  _resolve(value) {
    // 状态确定了,就不再发生变化了
    if (this._state !== 'pending') return
    // 推入微任务
    this._nextTick(() => {
      this._state = 'fulfilled'
      this._value = value
      this._queue.forEach((callback) => {
        callback.onFulfilled?.(this._value)
      })
    })
  }

  // 失败reject
  _reject(error) {
    // 状态确定了,就不再发生变化了
    if (this._state !== 'pending') return
    // 推入微任务
    this._nextTick(() => {
      this._state = 'rejected'
      this._value = error
      this._queue.forEach((callback) => {
        callback.onRejected?.(this._value)
      })
    })
  }
  ...
}
effect demonstration

b. Return Promise for chain call

Usually Promise will process multiple asynchronous requests, and sometimes the requests are interdependent.

E.g:

const getUser = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve({
        userId: '123'
      })
    }, 500)
  })
}

const getDataByUser = (userId) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      // ....
      resolve({a: 1})
    }, 500)
  })
}

// 使用
getUser().then((user) => {
  return getDataByUser(user.userId)
}).then((res) => {
  console.log(res)// {a: 1}
})

getDataByUser relies on getUser . Here we need to use the Promise chain call. Let’s change our code.

class Promise {
  constructor(fn) {
    fn(this._resolve.bind(this), this._reject.bind(this))
  }
  ...
  // 1. 这时候then方法需要返回新的Promise了,因为需要进行链式调用,并且下一个then方法接受上一个then方法的值
  // 2. 返回的Promise肯定是一个新的Promise,不然就会共用状态跟返回结果了。
  // 3. 把上一个then方法中的返回值当做下一个Promise resolve的值
  then(onFulfilled, onRejected) {
    // 返回新的Promise
    return new Promise((resolve, reject) => {
      // 有可能已经resolve了,因为Promise可以提前resolve,然后then方法后面注册,这个时候可以直接把值返给函数就好了
      if (this._state === 'fulfilled' && onFulfilled) {
        this._nextTick(onFulfilled.bind(this, this._value))
        return
      }
      if (this._state === 'rejected' && onRejected) {
        this._nextTick(onRejected.bind(this, this._value))
        return
      }
      /* 
        把当前Promise的then方法的参数跟新的Promise的resolve, reject存到一起,以此来做关联。
        这样就能把上一个Promise中的onFulfilled与新的Promise中的resolve两个关联到一块,然后便可以做赋值之类的操作了。reject同理
      */
      this._queue.push({
        onFulfilled,
        onRejected,
        resolve,
        reject
      })
    })
  }
  // reject同理
  _resolve(value) {
    // 状态确定了,就不再发生变化了
    if (this._state !== 'pending') return

    // 上面示例里面其实返回的是一个Promise,而不是直接返回的值,所以,这里我们需要做一个特殊处理。
    // 就是resolve()的值如果是Promise的对象,我们需要解析Promise的结果,然后在把值传给resolve
    if (typeof value === 'object' && typeof value.then === 'function') {
      // 我们可以把当前_resolve方法传递下去,因为then方法中的参数,一经下个Promise resolve,便会执行then方法对应的参数,然后把对应的值传入。
      // 这样就能取到Promise中的值
      // this._resove => obj.onFulfilled?.(this._value)
      // this._reject => obj.onRejected?.(this._value)
      value.then(this._resolve.bind(this), this._reject.bind(this))
      return
    }

    // 推入微任务
    this._nextTick(() => {
      this._state = 'fulfilled'
      this._value = value
      this._queue.forEach((obj) => {
        // 接受onFulfilled返回值
        const val = obj.onFulfilled?.(this._value)
        // reoslve这个值,此时 onFulfilled 是当前Promise then方法中的第一个参数: Promise.then((res) => {consolle.log(res)})
        // obj.resolve是新的Promise的resolve函数,这样就把then方法中的返回值传给下一个Promise
        obj.resolve(val)
      })
    })
  }
  ...
}
effect demonstration

Calling logic:

  1. process.nextTick are implemented using MutationObserver
  2. Promise chain called here by the then method (onFulfilled, onRejected) parameters and returns the new Promise in (resolve, reject) together association.
  3. Once the last Promise successful, call the onFulfilled function, you can put onFulfilled into the resolve of the new Promise.
  4. If you encounter resolve value is Promise objects, recursive resolution, then put the value returned out

Complete code

class Promise {
  _value
  _state = 'pending'
  _queue = []
  constructor(fn) {
    if (typeof fn !== 'function') {
      throw new Error('Promise resolver undefined is not a function')
    }
    /* 
      new Promise((resolve, reject) => {
        resolve: 成功
        reject: 失败
      })
    */
    fn(this._resolve.bind(this), this._reject.bind(this))
  }

  // 接收1-2参数,第一个为成功的回调,第二个为失败的回调
  then(onFulfilled, onRejected) {
    // 返回新的Promise
    return new Promise((resolve, reject) => {
      // 有可能已经resolve了,因为Promise可以提前resolve,然后then方法后面注册,这个时候可以直接把值返给函数就好了
      if (this._state === 'fulfilled' && onFulfilled) {
        this._nextTick(onFulfilled.bind(this, this._value))
        return
      }
      if (this._state === 'rejected' && onRejected) {
        this._nextTick(onRejected.bind(this, this._value))
        return
      }
      // 把当前Promise的then方法的参数跟新的Promise的resolve, reject存到一起,以此来做关联
      this._queue.push({
        onFulfilled,
        onRejected,
        resolve,
        reject
      })
    })
  }

  // 接收失败的回调
  catch(onRejected) {
    return this.then(null, onRejected)
  }

  // 成功与失败都执行的回调
  finally(onDone) {
    return this.then((value) => {
      onDone()
      return value
    }, (value) => {
      // console.log(value)
      onDone()
      throw value
    })
  }

  // 推入微任务
  _nextTick(fn) {
    if (typeof MutationObserver !== 'undefined') { // 浏览器
      // 这块可以单独拿出来共用,避免不必要的开销,不然每次都需要生成节点。
      const observer = new MutationObserver(fn)
      let count = 1
      const textNode = document.createTextNode(String(count))
      observer.observe(textNode, {
        characterData: true
      })
      textNode.data = String(++count)
    } else if (typeof process.nextTick !== 'undefined') { // node
      process.nextTick(fn)
    } else {
      setTimeout(fn, 0)
    }
  }
  // 成功resolve
  _resolve(value) {
    // 状态确定了,就不再发生变化了
    if (this._state !== 'pending') return

    // 上面示例里面其实返回的是一个Promise,而不是直接返回的值,所以,这里我们需要做一个特殊处理。
    // 就是如果resolve()的如果是Promise的对象,我们需要解析Promise的结果,然后在把值传给resolve
    if (typeof value === 'object' && typeof value.then === 'function') {
      // 我们可以把当前_resolve方法传递下去,因为then方法中的参数,一经下个Promise resolve,便会执行then方法对应的参数,然后把对应的值传入。
      // 这样就能取到Promise中的值
      // this._resove => obj.onFulfilled?.(this._value)
      // this._reject => obj.onRejected?.(this._value)
      value.then(this._resolve.bind(this), this._reject.bind(this))
      return
    }

    // 推入微任务
    this._nextTick(() => {
      this._state = 'fulfilled'
      this._value = value
      this._queue.forEach((obj) => {
        // 使用try catch 来捕获onFulfilled存在函数内部错误的情况
        try {
          // 接受onFulfilled返回值,如果不存在,把this._value往下传递
          const val = obj.onFulfilled ? obj.onFulfilled(this._value) : this._value
          // reoslve这个值,此时 onFulfilled 是当前Promise then方法中的第一个参数: Promise.then((res) => {consolle.log(res)})
          // obj.resolve是新的Promise的resolve函数,这样就把then方法中的返回值传给下一个Promise
          obj.resolve(val)
        } catch (e) {
          obj.reject(e)
        }
      })
    })
  }

  // 失败reject
  _reject(error) {
    if (this._state !== 'pending') return
    this._nextTick(() => {
      this._state = 'rejected'
      this._value = error
      this._queue.forEach((obj) => {
        try {
          const val = obj.onRejected ? obj.onRejected(this._value) : this._value
          // 当前 reject执行完毕之后,会返回新的Promise,应该是能正常resolve的,所以这里要用 resolve, 不应该继续使用reject来让下个Promise执行失败流程
          obj.resolve(val)
        } catch (e) {
          obj.reject(e)
        }
      })
    })
  }
}

Declare the static method of Promise

There are 4 static methods in total: Promise.resolve , Promise.reject , Promise.all , Promise.race , and they all return new Promises.

class Promise {
  ...
  /**
   * 直接resolve
   */
  static resolve(value) {
    // 是Promise直接返回
    if (value instanceof Promise) {
      return value
    } else if (typeof value === 'object' && typeof value.then === 'function') {
      // 传入的对象含有then方法
      const then = value.then
      return new Promise((resolve) => {
        then.call(value, resolve)
      })
    } else {
      // 正常返回值,直接返回新的Promise在resolve这个值
      return new Promise((resolve) => resolve(value))
    }
  }

  /**
   * 直接reject, 测试下Promise.reject并没做特殊处理,所以直接返回即可。
   */
  static reject(value) {
    return new Promise((resolve, reject) => reject(value))
  }

  /**
   * 传入数组格式的`Promise`并返回新的`Promise`实例,成功便按照顺序把值返回出来,其中一个失败则直接变成失败
   */
  static all(promises) {
    return new Promise((resolve, reject) => {
      let count = 0
      let arr = []
      // 按照对应的下标push到数组里面
      promises.forEach((promise, index) => {
        // 转换成Promise对象
        Promise.resolve(promise).then((res) => {
          count++
          arr[index] = res
          if (count === promises.length) {
            resolve(arr)
          }
        }, err => reject(err))
      })
    })
  }
  
  /**
   * 传入数组格式的`Promise`并返回新的`Promise`实例,成功与失败取决第一个的完成方式
   */
  static race(promises) {
    return new Promise((resolve, reject) => {
      promises.forEach((promise, index) => {
        // 转换成Promise对象
        Promise.resolve(promise).then((res) => {
          // 谁先执行直接resolve, 或reject
          resolve(res)
        }, err => reject(err))
      })
    })
  }
  ...
}

Promise implementation complete code

class Promise {
  _value
  _state = 'pending'
  _queue = []
  constructor(fn) {
    if (typeof fn !== 'function') {
      throw new Error('Promise resolver undefined is not a function')
    }
    /* 
      new Promise((resolve, reject) => {
        resolve: 成功
        reject: 失败
      })
    */
    fn(this._resolve.bind(this), this._reject.bind(this))
  }

  /**
   * 接收1-2参数,第一个为成功的回调,第二个为失败的回调
   *
   * @param {*} onFulfilled
   * @param {*} onRejected
   * @return {*} 
   * @memberof Promise
   */
  then(onFulfilled, onRejected) {
    // 返回新的Promise
    return new Promise((resolve, reject) => {
      // 有可能已经resolve了,因为Promise可以提前resolve,然后then方法后面注册,这个时候可以直接把值返给函数就好了
      if (this._state === 'fulfilled' && onFulfilled) {
        this._nextTick(onFulfilled.bind(this, this._value))
        return
      }
      if (this._state === 'rejected' && onRejected) {
        this._nextTick(onRejected.bind(this, this._value))
        return
      }
      // 把当前Promise的then方法的参数跟新的Promise的resolve, reject存到一起,以此来做关联
      this._queue.push({
        onFulfilled,
        onRejected,
        resolve,
        reject
      })
    })
  }

  /**
   * 接收失败的回调
   *
   * @param {*} onRejected
   * @return {*} 
   * @memberof Promise
   */
  catch(onRejected) {
    return this.then(null, onRejected)
  }

  /**
   * 成功与失败都执行的回调
   *
   * @param {*} onDone
   * @return {*} 
   * @memberof Promise
   */
  finally(onDone) {
    return this.then((value) => {
      onDone()
      return value
    }, (value) => {
      onDone()
      // 直接报错,可以在try catch中捕获错误
      throw value
    })
  }

  /**
   * 直接resolve
   *
   * @static
   * @param {*} value
   * @return {*} 
   * @memberof Promise
   */
  static resolve(value) {
    if (value instanceof Promise) {
      return value
    } else if (typeof value === 'object' && typeof value.then === 'function') {
      // 传入的对象含有then方法
      const then = value.then
      return new Promise((resolve) => {
        then.call(value, resolve)
      })
    } else {
      return new Promise((resolve) => resolve(value))
    }
  }

  /**
   * 直接reject, 测试下reject在Promise.reject中没做特殊处理
   *
   * @static
   * @param {*} value
   * @return {*} 
   * @memberof Promise
   */
  static reject(value) {
    return new Promise((resolve, reject) => reject(value))
  }

  /**
   * 传入数组格式的`Promise`并返回新的`Promise`实例,成功便按照顺序把值返回出来,其中一个失败则直接变成失败
   *
   * @static
   * @param {*} promises
   * @memberof Promise
   */
  static all(promises) {
    return new Promise((resolve, reject) => {
      let count = 0
      let arr = []
      if (Array.isArray(promises)) {
        if (promises.length === 0) {
          return resolve(promises)
        }
        promises.forEach((promise, index) => {
          // 转换成Promise对象
          Promise.resolve(promise).then((res) => {
            count++
            arr[index] = res
            if (count === promises.length) {
              resolve(arr)
            }
          }, err => reject(err))
        })
        return
      } else {
        reject(`${promises} is not Array`)
      }
    })
  }
  
  /**
   * 传入数组格式的`Promise`并返回新的`Promise`实例,成功与失败取决第一个的完成方式
   *
   * @static
   * @param {*} promises
   * @return {*} 
   * @memberof Promise
   */
  static race(promises) {
    return new Promise((resolve, reject) => {
      if (Array.isArray(promises)) {
        promises.forEach((promise, index) => {
          // 转换成Promise对象
          Promise.resolve(promise).then((res) => {
            resolve(res)
          }, err => reject(err))
        })
      } else {
        reject(`${promises} is not Array`)
      }
    })
  }

  // 推入微任务
  _nextTick(fn) {
    if (typeof MutationObserver !== 'undefined') { // 浏览器
      // 这块可以单独拿出来共用,避免不必要的开销,不然每次都需要生成节点。
      const observer = new MutationObserver(fn)
      let count = 1
      const textNode = document.createTextNode(String(count))
      observer.observe(textNode, {
        characterData: true
      })
      textNode.data = String(++count)
    } else if (typeof process.nextTick !== 'undefined') { // node
      process.nextTick(fn)
    } else {
      setTimeout(fn, 0)
    }
  }
  // 成功resolve
  _resolve(value) {
    // 状态确定了,就不再发生变化了
    if (this._state !== 'pending') return

    // 上面示例里面其实返回的是一个Promise,而不是直接返回的值,所以,这里我们需要做一个特殊处理。
    // 就是如果resolve()的如果是Promise的对象,我们需要解析Promise的结果,然后在把值传给resolve
    if (typeof value === 'object' && typeof value.then === 'function') {
      // 我们可以把当前_resolve方法传递下去,因为then方法中的参数,一经下个Promise resolve,便会执行then方法对应的参数,然后把对应的值传入。
      // 这样就能取到Promise中的值
      // this._resove => obj.onFulfilled?.(this._value)
      // this._reject => obj.onRejected?.(this._value)
      value.then(this._resolve.bind(this), this._reject.bind(this))
      return
    }

    // 通过打印测试,如果直接在线程里进行resolve, 状态跟值好像是直接就改变了,并没有执行完主流程,在执行微任务的时候进行修改的。
    // 所以把状态改变和值的修改移出了微任务,只有在走回调的时候才通过微任务进行处理
    this._state = 'fulfilled'
    this._value = value

    // 推入微任务
    this._nextTick(() => {
      this._queue.forEach((obj) => {
        // 使用try catch 来捕获onFulfilled存在函数内部错误的情况
        try {
          // 接受onFulfilled返回值,如果不存在,把this._value往下传递
          const val = obj.onFulfilled ? obj.onFulfilled(this._value) : this._value
          // reoslve这个值,此时 onFulfilled 是当前Promise then方法中的第一个参数: Promise.then((res) => {consolle.log(res)})
          // obj.resolve是新的Promise的resolve函数,这样就把then方法中的返回值传给下一个Promise
          obj.resolve(val)
        } catch (e) {
          obj.reject(e)
        }
      })
    })
  }

  // 失败reject
  _reject(error) {
    if (this._state !== 'pending') return
    this._state = 'rejected'
    this._value = error

    this._nextTick(() => {
      this._queue.forEach((obj) => {
        try {
          // 用户传入的函数内部错误捕获
          if (obj.onRejected) {
            const val = obj.onRejected(this._value)
            // 当前 reject执行完毕之后,会返回新的Promise,应该是能正常resolve的,所以这里要用 resolve, 不应该继续使用reject来让下个Promise执行失败流程
            obj.resolve(val)
          } else {
            // 递归传递reject错误
            obj.reject(this._value)
          }
        } catch (e) {
          obj.reject(e)
        }
      })
    })
  }
}

complete demonstration effect

Original blog address

The complete code of this project: GitHub

The above is the realization of Promise. Of course, this is different from the Promises/A+ specification. This is just for learning purposes.

QQ groupthe public
Front end miscellaneous group
QQ群:810018802
Winter Melon Bookstore
公众号:冬瓜书屋
I have created a new group to learn from each other. Whether you are a Xiaobai who is about to enter the pit, or a classmate who is halfway into the industry, I hope we can share and communicate together.
QQ group: 810018802, click to join

咚子
3.3k 声望12.5k 粉丝

一个前端