09月17, 2024

Promise A+ 规范和解读(Promise的前世今生)

规范的产生背景


该规范产生于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. onFulfilledonRejected 都是可选参数:

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

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

  2. 如果 onFulfilled 是一个函数:

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

    该函数只能被调用一次

  3. 如果 onRejected 是一个函数:

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

    该函数只能被调用一次

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

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

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

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

promise2 = promise1.then(onFulfilled, onRejected);

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

在执行onFulfilledonRejected 时,如果执行的过程中没有发生错误,并且返回值是 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,如果读取发生异常,则直接让promise失败,失败原因就是抛出的错误,然后结束处理即可
    2. 如果x.then是一个function
      1. 调用x.then方法:x.then(onFulfilled, onRejected)
      2. onFulfilled执行时,如果得到的数据是y,则执行[[Resolve]](promise, y)
      3. onRejected执行时,如果得到的失败原因是error,则让promise变成失败状态,原因是error
    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     
  })
})
  1. 关于Promise的catch函数

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

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

  1. 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函数
}
  1. 关于Promise的状态

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

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

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

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

本文链接:https://901web.com/post/what_is_Promise_in_javascript.html

-- EOF --

Comments

请在后台配置评论类型和相关的值。