Promises-A+规范

1035次阅读  |  发布于4年以前

概述

自从准备晋级之后,就拖更了很久了,既然晋级弄完了,那么也恢复更新了。

在面试别人的过程中,发现基本上没有人对整个Promise完全了解,因此希望通过这篇文章来帮助大家了解下Promise的全貌。本文的主要内容是Promise/A+规范的译文,主要是能够帮助大家了解下Promise的规范,以及解决在日常运用中遇到的一些问题。

原文地址为:https://promisesaplus.com/#point-1

如果需要测试一个Promise库是否符合Promise/A+规范,可以使用这个:https://github.com/promises-aplus/promises-tests。

原文概览

一个标准声明、可操作的JavaScript Promise——出自开发者,为了开发者。

一个promise代表一个异步操作的最终结果。与promise交互的主要方法是通过它的then函数。这个函数注册一个回调函数来接收promise最终的值(value)或者是没有成功的原因(reason)。

这篇文档详细说明了then方法的具体行为,它为所有的符合Promise/A+规范的实现提供了一个基础的交互。像这样,这个规范应该设计的非常稳定。尽管Promise/A+组织可能偶尔通过一些向后兼容的小修改来调整这个文档来适应新发现的场景,我们只会在经历过了小心的思考、讨论和测试后增加大的、不向后兼容的修改。

从历史上来看,Promise/A+阐述了更早的Promise/A的行为规范,并且在其基础上进行了扩展,覆盖了一些实际行为和之前遗漏的问题。

最终,核心的Promise/A+文档不关心如何去创建、完成(resolve)或者拒绝(reject)一个Promise,而是聚焦在提供一个可交互的then函数。在将来的其他规范中可能会涉及这些没有提及的内容。

1. 术语

1.1. "promise"是一个对象或者函数,它拥有一个符合文档中描述行为的then方法。

1.2. "thenable"是一个有then方法的对象或者函数。

1.3. "value"是一个合法的JavaScript值(包括undefined, 一个thenable或者一个promise)。

1.4. "exception"是一个使用在throw语句中的抛出来的值。

1.5. "reason"是一个用来表示promise拒绝的原因的值。

2. 要求

2.1 promise状态

一个promise必须处于一下三种状态:pending、fulfilled或者rejected。

2.1.1. 当处于pending状态时,promise:

2.1.1.1. 可能会转换成任何其他的状态。

2.1.2. 当处于fulfilled状态时,promise:

2.1.2.1. 禁止转换成其他状态。

2.1.2.2. 必须有一个无法更改的值。

2.1.3. 当处于rejected状态时,promise:

2.1.3.1. 禁止转换成其他状态。

2.1.3.2. 必须有一个无法更改的原因。

在这里,无法更改意味着全等(例如===),但是不代表深比较相等。

2.2 then方法

promise必须包含一个then方法来访问它当前或者最终的值或者原因。

Promise的then方法接收两个参数:

promise.then(onFulfilled, onRejected)

2.1.1. onFulfilledonRejected函数有都有可选的参数:

2.2.1.1. 如果onFulfilled不是一个函数,那么它必须被忽略掉。

2.2.1.2. 如果onRejected不是一个函数,那么它必须被忽略掉。

2.2.2. 如果onFulfilled是一个函数:

2.2.2.1. 它必须在promise到fulfilled状态后触发,promise的值是它的第一个参数。

2.2.2.2. 它在一个promise到fulfilled状态之前禁止被触发。

2.2.2.3. 它禁止被触发多次。

2.2.3. 如果onRejected是一个函数:

2.2.3.1. 它必须在promise到rejected状态后触发,promise的原因是它的第一个参数。

2.3.2.2. 它在一个promise到rejected之前禁止被触发。

2.3.2.3. 它禁止被触发多次。

2.2.4. onFulfilled或者onRejected只有在执行上下文堆栈只有平台代码时才能被触发。

2.2.5. onFulfilledonRejected必须作为函数被调用(例如没有this值)。

2.2.6. then方法可能在相同的promise中被调用多次。

2.2.6.1. 如果promise到了fullfilled状态,那么所有的onFulfilled回调函数都必须按照他们原有的顺序进行调用执行。

2.2.6.2. 如果promise到了rejected状态,那么所有的onRejected回调函数都必须按照他们原有的顺序进行调用执行。

2.2.7. then方法必须返回一个promise:

promise2 = promise1.then(onFulfilled, onRejected);

2.2.7.1. 如果onFulfilled或者onRejected方法返回一个值x,那么执行promise的解析过程[[Resolve]](promise2, x)

2.2.7.2. 如果onFulfilled或者onRejected方法抛出一个异常epromise2必须使用e作为原因拒绝掉(rejected)。

2.2.7.3. 如果onFulfilled不是一个函数并且promise1到了fulfilled状态,那么promise2必须在与promise1的值相同的情况下转换到fulfilled状态。

2.2.7.4. 如果onRejected不是一个函数并且promise1到了rejected状态,那么promise2必须在与promise1的原因相同的情况下转换到rejected状态。

2.3. promise解析函数

promise解析函数是一个输入一个promise或者一个值的抽象的操作,我们表示为[[Resolve]](promise, x)。如果x是一个thenable对象,在假定x的行为至少有点像一个promise的情况下,它会尝试让promise转换到x的状态。否则,他会用x的值完成promise的状态。

这种thenable对象的方式允许promise实现交互,只要他们暴露一个符合Promise/A+规范的then函数。它还允许Promise/A+的实现支持一个有合适的then方法的不兼容的实现。

运行[[Resolve]](promise, x),需要遵循以下步骤:

2.3.1. 如果promisex指向同一个对象,那么用TypeError作为原因拒绝promise。

2.3.2. 如果x是一个promise,判断它的状态:

2.3.2.1. 如果x是pending状态,promise保留pending状态直到x变成fulfilled状态或者rejected状态。

2.3.2.2. 如果x是fulfilled状态,那么用同样的值将整个promise完成。

2.3.2.3. 如果x是rejected状态,那么用同样的原因拒绝promise

2.3.3. 否则,如果x是一个对象或者函数,

2.3.3.1. 让then变成x.then

2.3.3.2. 如果在检测x.then这个属性的结果时抛出一个异常e,把e作为原因拒绝promise

2.3.3.3. 如果then是一个函数,那么用x作为this来调用它,第一个参数是resolvePromise,第二个参数是rejectPromise

2.3.3.3.1. 如果resolvePromise被值y调用,那么运行[[Resolve]](promise, y)

2.3.3.3.2. 如果rejectPromise被原因r触发,那么用r来拒绝promise

2.3.3.3.3. 如果resolvePromiserejectPromise都被调用,或者使用相同的参数多次调用,那么第一次调用生效,其他之后的任何调用都忽略掉。

2.3.3.3.4. 如果在调用then方法时抛出了一个异常e,

2.3.3.3.4.1. 如果resolvePromiserejectPromise已经被调用了那么就忽略掉它。

2.3.3.3.4.2. 否则,使用e作为原因拒绝promise

2.3.3.4. 如果then不是一个函数,那么用x完成promise

2.3.4. 如果x不是一个对象或者函数,那么用x完成promise

如果一个promise是通过在环形的thenable链中的一个thenable来完成的,如递归的[[Resolve]](promise, thenable)类型再次调用[[Resolve]](promise, thenable),遵循上述的规则会导致无穷递归。对这种递归情况的检测并且使用TypeError作为原因进行拒绝,我们鼓励实现,但不要求。

3. 注意事项

3.1. 在这里"平台代码"(platform code)意味着引擎,环境和promise实现代码。在实践中,这个要求确保了onFulfilledonRejected是异步执行的,then调用也是在循环之后,有一个新的堆栈信息。这可以通过一个宏任务(macro-task)机制例如setTimeout或者setImmediate来实现,也可以通过一个微任务(micro-task)例如MutationObserver或者process.nextTick来实现。如果promise的实现考虑平台代码,那么它自己可能会带一个任务执行队列或者“蹦床”来处理被调用情况。

3.2. 在严格模式下,this是在promise里面将会是undefined。在松散模型下,他代表的是全局对象。

3.3. 如果实现满足所有要求的话,可以允许promise2 === promise1。每一个实现都应该表明是否支持promise2 === promise1,如果支持则是需要在什么条件下。

3.4. 通常来说,如果按照当前的实现方式,我们只能知道x是一个真的promise。这一条允许你在具体实现的使用过程中来判断未知的promise的状态。

3.5. 在程序中,首先存储x.then的引用,其次测试这个引用,然后再调用这个引用,避免多次访问x.then属性。这样的预防措施对于确保那些会在两次访问之间可能变化的属性值获取到一致的结果非常重要。

3.6. 实现中不应该对thenable调用链值设置任意深度限制,而是应该假设这个递归的限制值是无穷大。只有真正的循环才会导致TypeError;如果遇到了一个长度为无穷大的不同的thenable,保证在正确的行为下一直递归。

总结

本文主要通过英文翻译为中文的Promise/A+规范,让大家了解了整个规范的全部内容。我会在下一篇博客中给大家带来如何实现一个完全符合Promise/A+规范的Promise。

Copyright© 2013-2020

All Rights Reserved 京ICP备2023019179号-8