首页 > 其他分享 >Promise规范和应用

Promise规范和应用

时间:2023-02-20 10:47:25浏览次数:49  
标签:resolve 规范 value reason Promise 应用 reject promise onRejected

1、参考资料

1、 promise A plus规范(opens new window)
2、【第1738期】100 行代码实现 Promises/A+ 规范(opens new window)
3、JS常见问题整理
4、史上最详细手写promise教程(opens new window)

2、PromiseA+规范

参考规范: promise A plus(opens new window),解读如下:

0、术语

  • promise是一个有then方法的对象或者函数,行为遵循本规范
  • thenable是一个then方法的对象或者是函数
  • value是promise状态成功时的值,也就是resolve的参数,指各种js值(包括undefined、thenable和promise)
  • reason是promise状态失败时的值,也就是reject的参数,表示拒绝的原因
  • exception是一个使用throw抛出的异常值

1、Promise States

promise有三种状态,注意他们之间的流转关系

1、pending

  • 初始状态,可改变
  • 一个promise在resolve或者reject之间都处于这个状态
  • 可通过resolve转变为fulfilled状态
  • 可通过reject转变为rejected状态

2、fulfilled

  • 最终态,不可变
  • 一个promise被resolve后会变为这个状态
  • 必须拥有一个value值

3、rejected

  • 最终态,不可变
  • 一个promise被reject后会变为这个状态
  • 必须拥有一个reason

状态流转如下:
pending -> resolve(value) -> fulfilled
pending -> reject(reason) -> rejected

2、then

promise应该有个then方法,用来访问最终的结果,无论是value还是reason。

promise.then(onFulfilled,onRejected)

1、参数要求

  • onFulfilled必须是函数类型,可选,如果不是函数,应该被忽略
  • onRejected必须是函数类型,可选,如果不是函数,应该被忽略

2、onFulfilled特性

  • 在promise变为fulfilled时,应该调用onFulfilled,参数时value
  • 在promise变为fulfilled之前,不应该被调用
  • 只能被调用一次(实现时需使用变量来限制执行次数)

3、onRejected特性

  • 在promise编程rejected时,应该调用onRejected,参数时reason
  • 在promise变成rejected之前,不应该被调用
  • 只能被调用一次

4、onFulfilled和onRejected应该时微任务

在执行上下文堆栈仅包含平台代码之前,不得调onFulfilled或onRejected函数,onFulfilled和onRejected必须被作为普通函数调用(即非实例化调用,这样函数内部this非严格模式下指向window),使用queueMicrotask或者setTimeout来实现微任务的调用

5、then方法可以被调用多次

  • promise状态变成fulfilled后,所有的onFulfilled回调都需要按照then的顺序执行,也就是按照注册顺序执行(实现时用数组存储多个onFulfilled的回调)
  • promise状态变成rejected后,所有的onRejected回调都需要按照then的顺序执行,也就是按照注册顺序执行(实现时用数组存储多个onRejected的回调)

6、then必须返回一个promise

then必须返回一个promise

promise2 = promise1.then(onFulfilled,onRejected)
  • onFulfilled或onRejected执行的结果是x,调用resolvePromise
  • 如果onFulfilled或者onRejected执行时抛出异常e,promise2需要被reject,其reason为e
  • 如果onFulfilled不是一个函数且promise1已经fulfilled,promise2以promise1的value触发onFulfilled
  • 如果onRejected不是一个函数且promise1已经rejected,promise2以promise1的reason触发onRejected

7、Promise的解决过程resolvePromise

resolvePromise(promise2, x, resolve, reject)
  • 如果x是当前promise本身(promise2和x相等),那么reject TypeError
  • 如果x是另一个promise(即x是一个promise),那么沿用它的state和result状态
    • 如果x是pending态,那么promise必须要在pending,直到x变成fulfilled或者rejected
    • 如果x是fulfilled态,用相同的value执行promise
    • 如果x是rejected太,用相同的reason拒绝promise
  • 如果x是一个object或者是一个function(不常见)
    • 首先取x.then的值,let then = x.then
    • 如果取x.then这步出错抛出e,那么以e为reason拒绝promise
    • 如果then是一个函数,将x作为函数的作用域this调用,即then.call(x, resolvePromise, rejectPromise),第一个参数叫resolvePromise,第二个参数叫rejectPromise
      • 如果resolvePromise以y为参数被调用,则执行resolvePromise(promise2, y, resolve, reject)
      • 如果rejectPromise以r为参数被调用,则以r为reason拒绝promise
      • 如果resolvePromise和rejectPromise都调用了,那么第一个调用优先,后面的调用忽略。
      • 如果调用then抛出异常e:若rejectPromise或rejectPromise已经被调用,那么忽略,否则以e为reason拒绝promise
    • 如果then不是一个function,以x为value执行promise
  • 如果x不是object或者function,以x为value执行promise

3、promise A+实现

1、定义三种状态,设置初始态,使用class实现类

const PENDING = 'pending'
const REJECTED = 'rejected'
const FULFILLED = 'fulfilled'
class MPromise {
  constructor() {
    this.status = PENDING
    this.value = null
    this.reason = null
    this.onResolveCallbacks = [] // pending时回调函数存储
    this.onRejectedCallbacks = []
  }
}

2、添加reject和resolve方法更改状态,执行回调

  reject(reason) {
    if(this.status === PENDING) {
      this.status = REJECTED
      this.reason = reason
      this.onRejectedCallbacks.forEach(fn => fn())
    }
  }

  resolve(value) {
    if(this.status === PENDING) {
      this.status = FULFILLED
      this.value = value
      this.onResolveCallbacks.forEach(fn => fn())
    }
  }

3、promise添加入参函数,函数同步执行,异常需reject

  constructor(fn) {
    this.status = PENDING
    this.value = null
    this.reason = null
    this.onResolveCallbacks = [] // pending时回调函数存储
    this.onRejectedCallbacks = []
    try {
      fn(this.resolve.bind(this), this.reject.bind(this))
    } catch (error) {
      this.reject(error)
    }
  }

4、实现then方法

1、入参为onFulfilled和onRejected并检查参数,如果不是function就忽略,原样返回value或者reason

2、最终返回promise,根据不同状态执行onFulfilled和onRejected,执行异常则reject,执行正常则执行resolvePromise(promise2, x, resolve, reject)。注意:rejected状态执行onRejected,Fulfilled状态执行onFulfilled,pending状态时收集回调至数组,待状态变更时执行,可使用ES6的getter和setter监听状态变换并执行,也可以直接放在resolve和reject方法中回调。

3、onFulfilled和onRejected是微任务,使用queueMicrotask包裹或setTimeout包裹

  isFunction(fn) {
    return typeof fn === 'function'
  }

  then(onFulfilled, onRejected) {
    // 入参为onFulfilled和onRejected并检查参数,如果不是function就忽略,原样返回value或者reason
    onFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => value
    onRejected = this.isFunction(onRejected) ? onRejected : err => { throw err }
    // 最终返回promise
    let promise2 = new MPromise((resolve, reject) => {
      // 根据不同状态执行onFulfilled和onRejected,执行异常则reject,执行正常返回x则执行resolvePromise
      if(this.status === FULFILLED) {
        // onFulfilled和onRejected执行是微任务
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value)
            this.resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0)
      }
    })
    if(this.status === REJECTED) {
      setTimeout(() => {
        try {
          let x = onRejected(this.reason)
          this.resolvePromise(promise2, x, resolve, reject)
        } catch (error) {
          this.reject(error)
        }
      }, 0)
    }
    // pending状态时收集回调至数组
    if(this.status === PENDING) {
      this.onResolveCallbacks.push(() => {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value)
            this.resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            this.reject(error)
          }
        }, 0);
      })

      this.onRejectedCallbacks.push(() => {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason)
            this.resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            this.reject(error)
          }
        }, 0)
      })
    }
  }

5、resolvePromise实现

  resolvePromise(newPromise, x, resolve, reject) {
    // 如果 newPromise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 newPromise
    // 这是为了防止死循环
    if(newPromise === x) {
      return reject(new TypeError('The promsie and the return value are the same'))
    }

    // 如果x是一个promise,那么沿用它的 state 和 result 状态
    if(x instanceof MPromise) {
      x.then((y) => {
        this.resolvePromise(newPromise, y, resolve, reject)
      }, reject)
    }

    // 如果x是一个对象或者函数,不为null
    if(x !== null && (typeof x === 'object' || this.isFunction(x))) {
      let then = null
      try {
        then = x.then
      } catch (error) {
        reject(error)
      }

      if(this.isFunction(then)) {
        // 添加是否调用过的标志
        let called = false
        try {
          then.call(
            x,
            (y) => {
              if(called) return
              called = true
              this.resolvePromise(newPromise, y, resolve, reject)
            },
            (r) => {
              if(called) return
              called = true
              reject(r)
            }
          )
        } catch (error) {
          if(called) return
          reject(error)
        }
      } else {
        resolve(x)
      }
    } else {
      resolve(x)
    }
  }

6、Promise.reject和Promise.resolve及catch实现

  catch(onRejected) {
    return this.then(null, onRejected)
  }

  static resolve(value) {
    if(value instanceof MPromise) {
      return value
    }
    return new MPromise(function(resolve) {
      resolve(value)
    })
  }

  static reject(reason) {
    return new MPromise(function(resolve, reject) {
      reject(reason)
    })
  }

7、promise.race和promise.all实现

  race(promiseList) {
    return new MPromise((resolve, reject) => {
      if(!Array.isArray(promiseList)) {
        reject('input param must be a list')
      }
      let size = promiseList.length
      if(size === 0) {
        resolve()
      } else {
        for (let i = 0; i < size; i++) {
          MPromise.resolve(promiseList[i]).then(
            (value) => {
              resolve(value)
            },
            (reason) => {
              reject(reason)
            }
          )
        }
      }
    })
  }

  all(promiseList){
    return new MPromise((resolve, reject) => {
      if(!Array.isArray(promiseList)) {
        reject('input param must be a list')
      }
      let size = promiseList.length
      if(size === 0) {
        resolve()
      } else {
        let count = 0
        let res = []
        for(let i = 0; i < size; i++) {
          MPromise.resolve(promiseList[i]).then((value) => {
            res[i] = value
            if(count++ === size) {
              resolve(res)
            }
          }).catch((reason) => {
            reject(reason)
          })
        }
      }
    })
  }

  // 返回所有promise的状态和结果
  allSettled(promiseList) {
    return new MPromise((resolve, reject) => {
      if(!Array.isArray(promiseList)) {
        return reject(new TypeError('arguments must be an array'))
      }
      let counter = 0
      const promiseNum = promiseList.length
      const resolveArray = []
      for (let i = 0; i < promiseNum; i++) {
        MPromise.resolve(promiseList[i]).then(value => {
          resolveArray[i] = {
            status: 'fulfilled',
            value
          }
        }).catch(reason => {
          resolveArray[i] = {
            status: 'rejected',
            reason
          }
        }).finally(() => {
          if(counter++ == promiseNum) {
            resolve(resolveArray)
          }
        })
      }
    })
  }

4、测试

使用promises-aplus-tests插件进行测试

/**
 * npm i promises-aplus-tests -g
 * 命令行 promises-aplus-tests [js文件名] 即可验证
 */
class MPromise {
  static deferred() {
    let dfd = {}
    dfd.promise = new MPromise((resolve, reject) => {
      dfd.resolve = resolve
      dfd.reject = reject
    })
    return dfd
  }
}

module.exports = MPromise

全量代码实现如下:

/**
 * npm i promises-aplus-tests -g
 * 命令行 promises-aplus-tests [js文件名] 即可验证
 */

const PENDING = 'pending'
const REJECTED = 'rejected'
const FULFILLED = 'fulfilled'
class MPromise {
  constructor(fn) {
    this.status = PENDING
    this.value = null
    this.reason = null
    this.onResolveCallbacks = [] // pending时回调函数存储
    this.onRejectedCallbacks = []
    try {
      fn(this.resolve.bind(this), this.reject.bind(this))
    } catch (error) {
      this.reject(error)
    }
  }

  reject(reason) {
    if(this.status === PENDING) {
      this.status = REJECTED
      this.reason = reason
      this.onRejectedCallbacks.forEach(fn => fn())
    }
  }

  resolve(value) {
    if(this.status === PENDING) {
      this.status = FULFILLED
      this.value = value
      this.onResolveCallbacks.forEach(fn => fn())
    }
  }

  isFunction(fn) {
    return typeof fn === 'function'
  }

  then(onFulfilled, onRejected) {
    // 入参为onFulfilled和onRejected并检查参数,如果不是function就忽略,原样返回value或者reason
    onFulfilled = this.isFunction(onFulfilled) ? onFulfilled : value => value
    onRejected = this.isFunction(onRejected) ? onRejected : err => { throw err }
    // 最终返回promise
    let promise2 = new MPromise((resolve, reject) => {
      // 根据不同状态执行onFulfilled和onRejected,执行异常则reject,执行正常返回x则执行resolvePromise
      if(this.status === FULFILLED) {
        // onFulfilled和onRejected执行是微任务
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value)
            this.resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        }, 0)
      }
    })
    if(this.status === REJECTED) {
      setTimeout(() => {
        try {
          let x = onRejected(this.reason)
          this.resolvePromise(promise2, x, resolve, reject)
        } catch (error) {
          this.reject(error)
        }
      }, 0)
    }
    // pending状态时收集回调至数组
    if(this.status === PENDING) {
      this.onResolveCallbacks.push(() => {
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value)
            this.resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            this.reject(error)
          }
        }, 0);
      })

      this.onRejectedCallbacks.push(() => {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason)
            this.resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            this.reject(error)
          }
        }, 0)
      })
    }
  }

  resolvePromise(newPromise, x, resolve, reject) {
    // 如果 newPromise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 newPromise
    // 这是为了防止死循环
    if(newPromise === x) {
      return reject(new TypeError('The promsie and the return value are the same'))
    }

    // 如果x是一个promise,那么沿用它的 state 和 result 状态
    if(x instanceof MPromise) {
      x.then((y) => {
        this.resolvePromise(newPromise, y, resolve, reject)
      }, reject)
    }

    // 如果x是一个对象或者函数,不为null
    if(x !== null && (typeof x === 'object' || this.isFunction(x))) {
      let then = null
      try {
        then = x.then
      } catch (error) {
        reject(error)
      }

      if(this.isFunction(then)) {
        // 添加是否调用过的标志
        let called = false
        try {
          then.call(
            x,
            (y) => {
              if(called) return
              called = true
              this.resolvePromise(newPromise, y, resolve, reject)
            },
            (r) => {
              if(called) return
              called = true
              reject(r)
            }
          )
        } catch (error) {
          if(called) return
          reject(error)
        }
      } else {
        resolve(x)
      }
    } else {
      resolve(x)
    }
  }

  catch(onRejected) {
    return this.then(null, onRejected)
  }

  static resolve(value) {
    if(value instanceof MPromise) {
      return value
    }
    return new MPromise(function(resolve) {
      resolve(value)
    })
  }

  static reject(reason) {
    return new MPromise(function(resolve, reject) {
      reject(reason)
    })
  }

  race(promiseList) {
    return new MPromise((resolve, reject) => {
      if(!Array.isArray(promiseList)) {
        reject('input param must be a list')
      }
      let size = promiseList.length
      if(size === 0) {
        resolve()
      } else {
        for (let i = 0; i < size; i++) {
          MPromise.resolve(promiseList[i]).then(
            (value) => {
              resolve(value)
            },
            (reason) => {
              reject(reason)
            }
          )
        }
      }
    })
  }

  all(promiseList){
    return new MPromise((resolve, reject) => {
      if(!Array.isArray(promiseList)) {
        reject('input param must be a list')
      }
      let size = promiseList.length
      if(size === 0) {
        resolve()
      } else {
        let count = 0
        let res = []
        for(let i = 0; i < size; i++) {
          MPromise.resolve(promiseList[i]).then((value) => {
            res[i] = value
            if(count++ === size) {
              resolve(res)
            }
          }).catch((reason) => {
            reject(reason)
          })
        }
      }
    })
  }

  // 返回所有promise的状态和结果
  allSettled(promiseList) {
    return new MPromise((resolve, reject) => {
      if(!Array.isArray(promiseList)) {
        return reject(new TypeError('arguments must be an array'))
      }
      let counter = 0
      const promiseNum = promiseList.length
      const resolveArray = []
      for (let i = 0; i < promiseNum; i++) {
        MPromise.resolve(promiseList[i]).then(value => {
          resolveArray[i] = {
            status: 'fulfilled',
            value
          }
        }).catch(reason => {
          resolveArray[i] = {
            status: 'rejected',
            reason
          }
        }).finally(() => {
          if(counter++ == promiseNum) {
            resolve(resolveArray)
          }
        })
      }
    })
  }

  static deferred() {
    let dfd = {}
    dfd.promise = new MPromise((resolve, reject) => {
      dfd.resolve = resolve
      dfd.reject = reject
    })
    return dfd
  }

}

module.exports = MPromise

标签:resolve,规范,value,reason,Promise,应用,reject,promise,onRejected
From: https://www.cnblogs.com/DTCLOUD/p/17136500.html

相关文章

  • Java工厂模式应用
    1.1案例需求假设对于学校使用仪器要进行网上收费,然后学校可根据不同主体,比如课题组或者个人进行收费。也具有不同的收费模式。那么对于原先的代码来说需要进行多个if-els......
  • Promise的then方法
     调用then方法then方法返回结果是Promise对象,对象状态由回调函数的执行结果决定如果回调函数中返回的结果是非promise类型的属性,状态为成功,返回值为对象的成功then方......
  • TiDB数据库在汽车之家的应用与实践
     引言TiDB是PingCAP公司研发的开源分布式关系型数据库,具有兼容MySQL协议,易水平扩展、高可用、强一致、HTAP等特性。目前TiDB已在汽车之家论坛,好友粉丝,智......
  • TiDB数据库在汽车之家的应用与实践
     引言TiDB是PingCAP公司研发的开源分布式关系型数据库,具有兼容MySQL协议,易水平扩展、高可用、强一致、HTAP等特性。目前TiDB已在汽车之家论坛,好友粉丝,智......
  • TiDB数据库在汽车之家的应用与实践
     引言TiDB是PingCAP公司研发的开源分布式关系型数据库,具有兼容MySQL协议,易水平扩展、高可用、强一致、HTAP等特性。目前TiDB已在汽车之家论坛,好友粉丝,智......
  • 前端模块化规范
    1.有哪些规范ES6、服务器端模块化规范:CommonJS、浏览器端模块化规范:AMD、CMD在ES6提出之前,JS社区已经尝试并提出了AMD、CMD、CommonJS等模块化规范,这......
  • python代码规范PEP8
    1、引言本文档给出了Python编码规约,主要Python发行版中的标准库即遵守该规约。对于C代码风格的Python程序,请参阅配套的C代码风格指南。本文档和PEP257(文档字......
  • TrueNAS添加应用管理目录
    添加目录   ......
  • Rainbond ubuntu20.04单主机(allinone)部署及简单应用构建
    1、Rainbond是什么?Rainbond是一个云原生应用管理平台,使用简单,不需要懂容器、Kubernetes和底层复杂技术,支持管理多个Kubernetes集群,和管理企业应用全生命周期。2、为什么......
  • MongoDB简介与应用场景、Docker安装Mongo、整合SpringBoot实现CRUD
    (目录)1MongoDB相关概念1.1业务应用场景传统的关系型数据库(如MySQL),在数据操作的“三高”需求以及应对Web2.0的网站需求面前,显得力不从心。解释:“三高”需求:•Hi......