浅析Promise(上)

"promise的前世今生"

Posted by Mzx on January 15, 2017

Promise的前世今生

Promise是什么?Promise是一种组织代码的规范,是一种约定。

Promise的兄弟姐妹

为了解决JavaScript的回调金字塔问题,于是CommonJS便推出了Promise/A规范。但是光有Promise/A并没有解决问题。于是便有了Promise/B,Promise/D,Promise/A+。其中Promise/A是基础。A描述了什么是Promise。但是光有Promsie/A并不能很好地工作。所以Promsie/BA的基础上扩展了一组API。有了这些API便又很多热心肠的大神开始生产各种Promise实现,为了让各种实现互相通用,变成相亲相爱的一家人,于是便有了Promise/DPromise/DPromise对象和Promise/B规范做了进一步约定。Promise/D使得各个Promise具体实现搭配起来更加默契。

但是实现一个Promise要遵循三个规范,太麻烦了!于是一个自称Promise/A+的组织基于Promise/A发布了一个新的规范Promise/A+Promise/A+规范了.then方法,并将其作为是否为Promise的判断条件。而且提供了一个兼容性测试工具。总的来说Promise/A+可以看做是Promise/A/D的合体。目前很多module都是基于Promise/A+实现的。如ES6 Promise

Promise/A+规范

Promise表示一个异步操作的最终结果,与之进行交互的主要是.then()方法,该方法接受两个回调函数,用于接受Promise的终值或者本Promise抛出的异常。

相关术语

  • Promise 一个拥有then方法的对象或者函数
  • value 指任何JavaScript的合法值(undefined,thenablePromise
  • exception 使用throw抛出的一个值
  • reason 表示一个Promise的拒绝原因

规范

Promise的状态

一个Promise当前状态必须是如下三种状态中的一种:Pending(等待态), Fulfilled(执行态), Rejected(拒绝态)

  • Pending 处在该状态时,Promise需满足如下条件:
    • 可以迁移至Fulfilled或者Rejected
  • Fulfilled 处在该状态的条件为:
    • 不能迁移至其他任何状态
    • 必须拥有一个不可变的终值
  • Rejected 处在该状态时的条件为:
    • 不能迁移志其他任何状态
    • 必须拥有一个不可变的reason

综上所述,Promise从创建到结束必须是以上三种状态之一,而且肯定是已Pending开始,Fulfilled或者Rejected结束。并且状态是不可逆转的。

Then方法

一个Promise必须提供一个then方法

promise.then(onFulfilled, onRejected)

  • 参数
    • onFulfilled 可选 (如果不是函数则被忽略)
    • onRejected 可选 (如果不是函数则被忽略)
  • onFulfilled 特性 (如果该参数是函数)
    • Promise 执行结束后必须被调用,第一个参数为Promise的终值
    • Promise执行结束前不可被调用
    • 执行次数不可超过一次
  • onRejected 特性 (如果该参数是函数)
    • Promise被拒绝后必须执行调用,第一个参数为Promise的拒绝原因
    • Promise被拒绝执行前不可调用
    • 执行次数不可超过一次
  • 调用时机
    onFulfilledonRejected 只有在执行环境堆栈仅包含平台代码时才可被调用

  • 调用要求
    onFulfilledonRejected 必须作为函数调用(即没有this

  • 多次调用 then方法可以被同一个Promise多次调用
    • Promise成功执行时,所有onFulfilled需按照其注册顺序依次回调
    • Promise被拒绝执行时,所有onRejected需按照其注册顺序依次回调
  • 返回 then方法必须返回一个Promise对象

promise2 = promise1.then(onFulfilled, onRejected)

  • onFulfilledonRejected返回一个x,则运行如下Promise解决过程: [[Resolve]](promise2, x)
  • onFulfilledonRejected返回一个异常,则promise2必须拒绝执行,并返回异常信息
  • onFulfilled不是函数并且promise1成功执行,则promise2必须成功执行并返回相同的值
  • onResolved不是函数,且promise1拒绝执行,promise2必须拒绝执行并返回相同的原因

Promise的解决过程

Promise的解决过程是一个抽象的操作,其需输入一个promsie和一个值,可以表示为[[Resolve]](promsie, x),若x的有then方法,且看上去像Promise,解决程序尝试让promise接受x的状态;否则使用x的值来执行promise

这种thenable的特性使得Promise 的实现可以更具通用性:只要其暴露出一个遵循Promise/A+规范的then方法即可;同时也使遵循Promise/A+规范的实现可以与那些不太规范但可用的实现良好共存。

运行[[Resolve]](promsie, x)须遵循如下步骤:

  1. xpromise相等
    xpromise指向同一对象,则拒绝执行promise并抛出TypeError异常
  2. xPrmise对象
    xPromise,则让promise接受x的状态:
    • xPending状态, promise需保持为Pending 直到 x被执行或者拒绝
    • xFulfilled, 用相同的值执行promise
    • xRejected, 用相同的拒因执行promise
  3. x 为对象或者函数
    • x.then赋值给then
    • 若取x.then的值抛出异常,则把该异常作为拒绝prmoise的原因
    • then是函数,则将x作为函数作用域this调用。传递两个回调函数作为参数,第一个参数叫做resolvePromise,第二叫做rejectPromise
      • resolvePromisey为参数被调用,则运行[[Resolve]](promise, y)
      • rejectProiser为原因被调用,则以r为原因拒绝promise
      • resolvePromiserejectPromise均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下调用
      • 若调用then抛出了异常e:
        • resolvePromiserejectPromise已经被调用,则忽略
        • 否则已e为原因拒绝promise
      • then 不是函数,以x为参数执行promise
    • x 不为对象或者函数,以x为参数执行promise

若一个promise被一个循环的thenable链中的对象解决,而[[Resolve]](promise, thenable)的递归性质又使得其被再次调用,根据上述算法将会陷入无限递归中。算法虽不强制要求,但也鼓励使用者检测这样的递归是否存在,若检测到存在则以一个可是别的TypeError 为原因来拒绝promise

参考文档

Promsie/A英文原文:http://wiki.commonjs.org/wiki/Promises/A

Promsie/B英文原文:http://wiki.commonjs.org/wiki/Promises/B

Promsie/D英文原文:http://wiki.commonjs.org/wiki/Promises/D

Promise/A+英文原文: https://promisesaplus.com/