Promise A+ 规范和解读

一开始,我想的是把 Promise A+ 的规范原文翻译过来即可

可是认真的看了一下原文,发现有些内容直接翻译过来不是特别容易理解

因此,决定对规范进行核心部分的翻译,然后再对其进行解读

原版的规范参考:https://promisesaplus.com/

规范的产生背景

该规范产生于2012年左右,它是 Promise A 规范的升级版

可以看出在出现时间上,Promise A+ 是早于 ES6 的

在那个时候,为了处理异步场景,JS中充斥着各种回调函数:

// 以回调模式处理异步button.addEventListener("click", function(e){  // 回调函数})

setTimeout(function(){  // 回调函数}, 1000)

过去,回调函数也不是唯一的处理异步的方式,有些异步场景也可以把函数保存到对象的属性中,以便将来调用:

// 保存函数的属性xhr.onreadstatechange = function(){

}

button.onclick = function(){

}

可以看出,过去处理异步的方式是不统一的!

在那个混乱的异步处理年代,Promise A+ 横空出世

不是一个技术,而是一套规范

它希望所有的前端开发者都遵循这套规范,使得所有的开发者书写的异步代码达到统一

后来,ES6 在制作 Promise 标准时,就参考了该规范

规范翻译

由于这一节是翻译的规范内容,比较抽象,如果看不懂可以直接跳到最后一节——解读

一个 Promise 对象应该能够表达一个异步操作,异步操作无论成功还是失败,总会有一个结果,这个结果可以通过它的then方法来进行交互,具体的交互方式是,注册一个回调函数到then方法中

该规范详细的描述了then方法的具体规格

Promise

promise 用于表示一个异步任务,它应该是一个带有 then 方法的对象

它有三种状态:pending 挂起,任务进行中fulfilled 完成rejected 失败

任何时刻,promise 一定处于三种状态之一

ES6 中的 Promise 完成状态是 resolved,单词不同,含义一样

  1. 当 promise 处于 pending 状态时:

    它可以在任何合适的时候把状态转变为fulfilledrejected

  2. 当 promise 处于 fulfilled 状态时:

    它无法再次更改到其他状态

    它必须拥有一个值,表示任务完成时的数据,该数据可以是任何 JS 数据(甚至是 undefined),并且这个值一旦确定下来,不可更改

  3. 当 promise 处于 rejected 状态时:

    它无法再次更改到其他状态

    它必须拥有一个值,表示任务失败的原因,该值可以是任何 JS 数据(甚至是 undefined),并且这个值一旦确定下来,不可更改

then 方法

promise 应该提供一个 then 方法,通过这个方法,我们可以访问到任务完成的值 或 任务失败的原因

then方法可以接收两个参数:

promise.then(onFulfilled, onRejected)
  1. onFulfilled 和 onRejected 都是可选参数:

    如果 onFulfilled 不是一个函数,它必须被忽略

    如果 onRejected 不是一个函数,它必须被忽略

  2. 如果 onFulfilled 是一个函数:

    它应该在 promise 到达 fulfilled 状态时被调用,调用该函数时,应该把任务完成时的值作为第一个参数传递进去

    该函数只能被调用一次

  3. 如果 onRejected 是一个函数:

    它应该在 promise 到达 rejected 状态时被调用,调用该函数时,应该把任务失败的原因作为第一个参数传递进去

    该函数只能被调用一次

  4. onFulfilled 和 onRejected必须要等到当前执行栈清空后才能被调用,换句话说,它们是异步执行的

  5. 可以多次对同一个promise调用then方法,从而注册多个onFulfilledonRejected

    当任务完成 或 任务失败时,将按照注册的顺序,依次调用注册的 onFulfilled 或 onRejected

  6. then方法必须再次返回一个promise

    promise2 = promise1.then(onFulfilled, onRejected);

    在执行onFulfilled 或 onRejected 时,如果执行的过程中报了一个错误,则会导致 promise2 进入 rejected 状态,并且和状态相关的失败原因就是抛出的错误

    在执行onFulfilled 或 onRejected 时,如果执行的过程中没有发生错误,并且返回值是 xx 可以是任何数据,包括undefined),则会进入 任务完成处理流程 [[Resolve]](promise2, x),该流程在下一个小节解释

    如果onFulfilled不是一个函数,同时promise1已经fulfilledpromise2也会自动变成fulfilled,完成的相关数据和promise1一致

    如果onRejected不是一个函数,同时promise1已经rejectedpromise2也会自动变成rejected,失败的原因和promise1一致

任务完成处理流程

这个流程是一个概念上的操作规范,而不是真实的代码

Promise A+ 期望实现该规范的代码也是要实现这个处理流程

为了便于说明,Promise A+ 把这个操作规范记作[[Resolve]](promise, x),它应该按照以下的流程进行:

  1. 如果promisex是同一个对象,则把promise设置为rejected状态,失败原因是一个TypeError
  2. 如果x是一个promise
    1. 如果x处于pendingpromise也必须处于pending,知道x完成或失败
    2. 如果x完成,promise也会完成,数据和x完成的数据一致
    3. 如果x失败,promise也会失败,原因和x失败原因一致
  3. 如果x是一个对象或函数:
    1. 调用x.then方法:x.then(onFulfilled, onRejected)
    2. onFulfilled执行时,如果得到的数据是y,则执行[[Resolve]](promise, y)
    3. onRejected执行时,如果得到的失败原因是error,则让promise变成失败状态,原因是error
    1. 读取x.then,如果读取发生异常,则直接让promise失败,失败原因就是抛出的错误,然后结束处理即可
    2. 如果x.then是一个function
    3. 如果x.then不是一个function, 把promise变成fulfilled状态,数据为x
    4. 如果调用x.then的时候发生错误,则把promise变成失败状态,失败原因为抛出的错误
  4. 如果x不是一个对象或函数,把promise变成fulfilled状态,数据为x

解读

如果你能读懂上一节的内容,这一部分对你来说就毫无意义

  1. ES6 的 Promise 和 Promise A+ 是一个什么样的关系?

    ES6 的 Promise 遵循的是 Promise A+ 的规范

    这也就意味着 ES6 的 Promise 完全可以和其他遵循了 Promise A+ 规范的 Promise 进行互操作

    比如,ES6 的 Promise 可以和 JQuery 的Promise进行互操作

    $.ajax(...).then(resp=>{    // 这是JQuery的Promise的then函数
      return new Promise(resolve=>{      // 这是ES6的Promise     
      })
    })
  2. 关于Promise的catch函数

    Promise A+ 规范中并没有规定需要提供catch,但ES6的Promise提供了这一函数,它是为了让开发者使用起来更加方便

    这并不代表ES6的Promise打破了这一规范,因为绝大部分规范都是只规定了你至少有什么东西,并不关心你多了什么东西

  3. Promise必须通过构造函数new Promise得到吗?

    Promise A+没有这样规定,它可以通过任何途径得到,只不过ES6的Promise是通过构造函数得到的

    Promise A+规范,要求Promise必须是一个对象或是一个函数,该对象或函数提供了满足要求的then方法即可

    比如,下面的代码得到的都是满足规范的Promise

    const promise1 = {
      then(onFufilled, onRejected){    // 满足规范的then函数
      }
    }const promise2 = function(a, b){return a+b}
    promise2.then = function(onFufilled, onRejected){  // 满足规范的then函数}
  4. 关于Promise的状态

    Promise A+ 并没有要求Promise必须提供什么属性来得到状态,这些都是Promise的内部信息,你可以对外提供,也可以不提供

    ES6的Promise提供了两个属性[[PromiseStatus]][[PromiseValue]],分别表示Promise的状态和状态的相关数据(成功的数据或失败的原因),但这两个属性均无法在外部访问

    另外,Promise A+ 也没有对状态的名称做出强制要求,在规范文档中,Promise A+ 使用了pendingfufilledrejected,但这些名称仅仅是为了说明规范而产生的,在具体的实现中,你可以自行规定任何的名称来表示三种状态

    ES6的Promise就使用了pendingresolvedrejected来表示三种状态,这并没有打破Promise A+规范


您可能还会对下面的文章感兴趣: