【译】前端知识储备——Promise/A+规范

概述

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

在面试别人的过程中,发现基本上没有人对整个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。

原文链接:https://promisesaplus.com/#point-1

原文作者:the Promises/A+ organization

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏企鹅号快讯

PHP中被忽略的性能优化利器:生成器

如果是做Python或者其他语言的小伙伴,对于生成器应该不陌生。但很多PHP开发者或许都不知道生成器这个功能,可能是因为生成器是PHP5.5.0才引入的功能,也...

38814
来自专栏PHP在线

48条高效率的PHP优化写法

1 字符串 1.1 少用正则表达式 能用PHP内部字符串操作函数的情况下,尽量用他们,不要用正则表达式, 因为其效率高于正则。 没得说,正则最耗性能。 str_...

64810
来自专栏信安之路

php 反序列漏洞初识

在 OWASP TOP10 中,反序列化已经榜上有名,但是究竟什么是反序列化,我觉得应该进下心来好好思考下。我觉得学习的时候,所有的问题都应该问 3 个问题:w...

2330
来自专栏PHP在线

PHP底层的运行机制与原理

PHP说简单,但是要精通也不是一件简单的事。我们除了会使用之外,还得知道它底层的工作原理。 PHP是一种适用于web开发的动态语言。具体点说,就是一个用C语言实...

1K6
来自专栏从流域到海域

《笨办法学Python》 第20课手记

《笨办法学Python》 第20课手记 本节课讲函数与文件,内容比较简单,但请注意常见问题解答,你应该记住那些内容。 指针表示存储地址。 原代码如下: from...

1996
来自专栏Python专栏

Python | 19个优雅的Python编程技巧

http://lovesoo.org/pythonic-python-programming.html

2783
来自专栏从零开始学 Web 前端

C/C++练习题(三)

分析:在我们看来,虽然使用字符数组和字符指针差不多,printf都可以打印出字符串出来,但是编译器对他们的处理完全不同。 对于字符指针,编译器看到后,...

6884
来自专栏微信终端开发团队的专栏

C# 内存管理机制及 WP 内存泄漏定位方法

C#内存管理机制及WP内存泄漏定位方法 一、C#的内存管理机制 1. 托管资源与非托管资源 什么是托管资源?托管资源通俗的理解就是,把资源交给.net去管理,这...

3917
来自专栏Albert陈凯

Stack and Heap 堆和栈的区别include

在和计算机内存打交道时,我们一定会碰到堆和栈,这两个东西很容易搞混,那么现在就来梳理一下二者的关系。 栈(Stack)是用来静态分配内存的而堆是动态分配内存的,...

2728
来自专栏从流域到海域

堆和栈的区别

堆(heap)和栈(stack) 在计算机领域,堆栈是一个不容忽视的概念,堆栈是两种数据结构。堆栈都是一种数据项按序排列的数据结构,只能在一端(称为栈顶(...

2137

扫码关注云+社区

领取腾讯云代金券