文档章节

Promises/A+规范

一配
 一配
发布于 2015/12/13 11:18
字数 2790
阅读 395
收藏 5
点赞 1
评论 2

翻译前言:搜索已经有多篇Promises/A+规范的中文翻译,可参见[4],[5]。但[4][5]翻译都难以理解,例如[4]中未保留原规范的章节编号,不容易对比原文。[4]中将reason翻译为"据因",但我理解他想要表达的是“拒因”,有一些文字中用的是“拒因”,但这样翻译很不容易理解。再比如[4]中将fulfilled状态翻译为执行态, 后面中文语句中也很难以理解。[5]中尽可能地将一些专用词汇没有翻译为中文,但错别字较多,行文不畅。本文参考了[4]与[5]的中文翻译,文中强调的must、must not突出翻译了出来,但尽可能保留了原规范的章节编号,术语、状态和一些特定词汇不翻译为中文,直接采用英语反倒使得文字简洁易懂。

下面是Promises/A+规范Version 1.1.1 /2014-05-05的中文翻译。


一个开放、健全且通用的 JavaScript Promise 标准。由开发者制定,供开发者参考。

Promise 表示一个异步操作的最终结果,与之进行交互的方式主要是 then 方法,该方法注册了两个回调函数,用于接收 promise 的最终值(eventual value)或promise 不能fulfilled的原因(reason)。

本规范详细描述then方法的行为特点,所有遵循 Promises/A+ 规范实现的 promise 均可以本标准作为参照基础来实现then方法。规范应当是十分稳定的。Promise/A+ 组织会不断修订本规范,以解决一些新发现的边边角角问题,但所做的改动会是微小且向后兼容的。如果我们要进行大规模或者向后不兼容的更新,我们一定会在事先进行谨慎地考虑、详尽的探讨和严格的测试。

从历史上说,本规范实际上是澄清了之前 Promise/A 规范 中建议的行为语句,扩展了这些建议中已成为事实上规范的行为,同时删减了原规范中一些不足以成为规范和有问题的部分。

最后,Promises/A+ 规范的核心不涉及如何创建、fulfill或reject promise,而是专注于提供一个可互操作的then方法。上述对于 promises 的操作方法将来在其他规范中可能会提及。

1. 术语

1.1 "promise" 是一个拥有 then 方法的对象或函数,该方法的行为遵循本规范.

1.2“thenable”是一个定义了 then 方法的对象或函数.

1.3“value”指任何 JavaScript 合法值(包括 undefined , thenable对象或promise对象).

1.4“exception”是使用 throw 语句抛出的一个value。

1.5“reason”是表示promise状态为何转换为rejected的一个value.

2. 需求

2.1. Promise状态

    Promise状态必须为pendingfulfilledrejected中的一种。

2.1.1. 当状态为pending时:

       2.1.1.1. 可以转换为fulfilled或rejected状态。

2.1.2. 当状态为fulfilled时:

       2.1.2.1. 一定不能转换为其他状态。

       2.1.2.2. 必须有一个value值,且必须不可改变

2.1.3. 当状态为rejected时:

   2.1.2.1. 一定不能转换为其他状态。

       2.1.2.2. 必须有一个reason,且必须不可改变。

    上述"必须不可改变“指的是恒等不变(immutable identity:即可用 === 判断相等),而不是意味着深不可变(deep immutability。译者注: 这里需要再研究下)。

2.2. then方法

    promise必须提供一个then方法,用于获取promise当前/最终value或reason。

    promise的then方法接受两个参数:

  promise.then(onFulfilled, onRejected)

2.2.1.  onFulfilled 和 onRejected 都是可选参数:

      2.2.1.1. 如果 onFulfilled 不是函数类型,则必须被忽略。

      2.2.1.2. 如果 onRejected 不是函数类型,则必须被忽略。

2.2.2.  如果 onFulfilled 是函数:

      2.2.2.1. 当promise状态为fulfilled时必须被调用,promise的value作为第一个参数。

      2.2.2.2. 当promise状态为fulfilled之前一定不能被调用。

      2.2.2.3. 调用次数一定不能超过一次。

2.2.3.  如果 onRejected 是函数:

      2.2.3.1. 当promise状态为rejected时必须被调用,promise的reason作为第一个参数。

      2.2.3.2. 当promise状态转换为rejected之前一定不能被调用。

      2.2.3.3. 调用次数一定不能超过一次

2.2.4.  onFulfilled 和 onRejected 只有当执行上下文堆栈中仅包含平台代码[3.1]时才可以被调用。

2.2.5.  onFulfilled 和 onRejected 必须作为函数来调用(即没有 this 值)[3.2]

2.2.6.  对同一个promise可以调用多次then方法,

      2.2.6.1. 如果/当promise状态为fulfilled时,必须按调用then方法的顺序依次执行 onFulfilled 回调函数。

      2.2.6.2. 如果/当promise状态为rejected时,必须按调用then方法的顺序依次执行 onRejected 回调函数。 

2.2.7. then方法必须返回一个promise[3.3]

promise2 = promise1.then(onFulfilled, onRejected);

      2.2.7.1. 如果 onFulfilled 或 onRejected 函数返回值为x,那么执行Promise resolution过程 [[Resolve]](promise2, x) 。

      2.2.7.2. 如果 onFulfilled 或 onRejected 函数抛出异常e,那么promise2状态为rejected, e 作为reason。

      2.2.7.3. 如果 onFulfilled 不是函数且promise1状态是fulfilled,那么promise2状态必须是fulfilled且与promise1的value相同。

      2.2.7.4. 如果 onRejected不是函数且promise1状态是rejected,那么promise2状态必须是rejected且与promise1的reason相同。

2.3. Promise resolution过程

     Promise resolution过程是一个抽象操作,需要输入一个promise和一个value,用 [[Resolve]](promise, x) 来表示。如果x是thenable对象,则尝试使promise接受x的状态,这里假定x的行为特性至少类似于一个promise;否则用value x来fulfill promise。

       这样处理then对象可以使promise的实现有更好的互操作: 只要它们暴露出一个遵循Promises/A+规范的then方法。这样处理也使得遵循Promise/A+ 规范的实现可以与那些未完整遵循规范但then方法合理的实现能良好共存。

     [[Resolve]](promise, x) 按照下面步骤来运行:

2.3.1. 如果promise和x指向同一个对象,则reject promise并且用一个TypeError作为reason。

2.3.2. 如果x是一个promise实例,则以x的状态作为promise的状态[3.4]

  2.3.2.1. 如果x的状态为pending,那么promise的状态必须保持为pending,直到x的状态变为fulfilled或者rejected。

      2.3.2.2. 如果/当x的状态为fulfilled,则用同样的value来fulfill promise。

      2.3.2.3. 如果/当x的状态为rejected,则用同样的reason来reject promise

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

      2.3.3.1. 将x.then赋值给then[3.5]

      2.3.3.2. 如果在获取x.then属性时抛出一个异常e,则用e作为reason来reject promise

      2.3.3.3. 如果then是函数类型,则以x作为then函数内部的this指针,以resolvePromise为第一个参数,rejectPromise为第二个参数,调用then函数。这里:

        2.3.3.3.1. 如果/当resolvePromise被调用且value为y,则执行[[Resolve]](promise, y) 。

            2.3.3.3.2. 如果/当rejectPromise被调用且reason为r,则用r来reject promise。

            2.3.3.3.3. 如果 resolvePromise 和 rejectPromise都被调用,或者多次调用的参数都相同,则优先采用首次调用并忽略剩下的调用。

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

      2.3.3.4.1. 如果resolvePromise或rejectPromise已经被调用,则忽略该异常。

      2.3.3.4.2. 否则用e作为reason来reject promise。

       2.3.3.4. 如果then不是函数类型,用x来fulfill promise

2.3.4.如果x不是对象或函数,用x来fulfill promise

    如果一个promise是被一个循环的 thenable 链中的thenable对象resolve,而 [[Resolve]](promise, thenable) 的递归性质又使得 [[Resolve]](promise, thenable) 被再次调用,根据上述算法将会陷入无限递归之中。本规范不强制要求,但鼓励实现者检测这样的递归是否存在,若检测到存在则以一个TypeError为reason来reject promise[3.6]。

3. 备注

3.1. 这里的“平台代码”是指引擎、执行环境和promise实现代码。实践中,这个需求是确保 onFulfilled 和 onRejected 函数为异步执行,且应该在 then 方法被调用的那一轮事件循环之后,在一个新的堆栈中执行。这个事件队列可以采用“宏任务(macro-task)”机制(诸如通过 setTimeout 或 setImmediate )或者“微任务(micro-task)”机制(诸如通过 MutationObserver 或 process.nextTick )来实现。由于 promise 的实现代码被视为平台代码,因此实现代码自身可以包含一个任务调度队列或者handler被调用的跳板


译者注: 这里提及了 macro-task 和 micro-task 两个概念,可参见[6], [7]。当挂起任务时,JS 引擎会将所有任务按照类别分到这两个队列中,事件循环的每一轮都要处理来自 macro-task 队列(这个队列在 WHATWG specification中被称为任务队列)的一个任务,该任务执行完毕后取出 micro-task 队列中的所有任务顺序执行;之后再是下一轮取出一个macro-task 任务,取出所有的micro-tasks,从而周而复始。

但这样可能会导致当micro-task 队列中所有任务运行完成,执行下一个macro-task 任务时已经过去了很长时间,这会导致UI被阻塞等各种异常。Node.js中process.nextTick函数会在micro-tasks中排队, 有一个内在保护机制process.maxTickDepth可以避免长时间阻塞,这个值缺省为1000, 当这个时间到后就不再处理micro-tasks,而是开始处理下一个macro-task

通常, 当需要以同步方式来做异步访问时使用micro-tasks(即立即执行这一micro任务),否则使用macro-tasks.

举例:

macro-tasks: setTimeout, setInterval, setImmediate, I/O, UI rendering
micro-tasks: process.nextTick, Promises, Object.observe, MutationObserver


3.2. 在strict模式下this指针为undefined,而在sloppy模式下this指针为全局对象。

3.3. 代码实现在满足所有需求点的情况下可以允许 promise2 === promise1 。每个实现都要有文档说明其是否允许以及在何种条件下允许 promise2 === promise1。

3.4. 一般来说,如果按照当前实现,只知道x是个真正的 promise 。这一规则允许那些特例实现 接受 遵循规范要求的Promises的状态。

3.5.  这步我们先是存储了一个指向 x.then 的引用,接下来测试该引用,然后调用该引用,以避免多次访问 x.then 属性。这种预防措施确保了访问该属性的一致性,因为其值可能在检索调用时被改变。

3.6. 实现不应当对thenale链的深度有任何限制, 不应当假定超过该限制就会无限递归。只有真正的循环递归才应能导致 TypeError 异常;如果一条无限长的链上有不同的thenable,那么不断递归就是正确的行为。

参考资料:

[1] Promises/A+, https://promisesaplus.com/

[2] Differences from Promises/A, https://promisesaplus.com/differences-from-promises-a

[3] Conformant Implementations, https://promisesaplus.com/implementations

[4] Promise A+ 规范 中文翻译, http://malcolmyu.github.io/malnote/2015/06/12/Promises-A-Plus/

[5] 前端翻译:Promises/A+规范, 肥仔John, http://www.cnblogs.com/fsjohnhuang/p/4139172.html

[6] Difference between microtask and macrotask within an event loop context, http://stackoverflow.com/questions/25915634/difference-between-microtask-and-macrotask-within-an-event-loop-context

[7] Promise进阶介绍+原生实现, http://wengeezhang.com/?p=11

© 著作权归作者所有

共有 人打赏支持
一配
粉丝 34
博文 130
码字总数 94295
作品 0
西城
加载中

评论(2)

南漂一卒
南漂一卒

引用来自“炳鑫”的评论

感谢一个,then返回什么东东始终确定不了。还是直接看规范好一些。再次感谢
promise.then() 返回一个新 Promise 对象,代表前者回调执行的结果~
炳鑫
感谢一个,then返回什么东东始终确定不了。还是直接看规范好一些。再次感谢
理解 Promise 的工作原理

Javascript 采用回调函数(callback)来处理异步编程。从同步编程到异步回调编程有一个适应的过程,但是如果出现多层回调嵌套,也就是我们常说的厄运的回调金字塔(Pyramid of Doom),绝对是一种...

黄金林 ⋅ 2016/12/20 ⋅ 0

异步编程之Javascript Promises 规范介绍

什么是 Promises Promises是一种关于异步编程的规范,目的是将异步处理对象和处理规则进行规范化,为异步编程提供统一接口。 传统的回调函数 说到JavaScript的异步编程处理,通常我们会想到回...

葡萄城控件技术团队 ⋅ 2016/04/19 ⋅ 0

初识JavaScript Promises

JavaScript有很多槽点,嵌套回调怕是千夫所指。 很久之前,我一直使用async来处理JavaScript异步编程中的嵌套回调问题。当然我也大概的了解过一些其它旨在解决这些问题的类库,诸如EventProx...

梵高 ⋅ 2014/06/29 ⋅ 15

手摸手带你从零实现一个符合Promises/A+规范的promise

前言 Promise 是异步编程的一种解决方案,比传统的解决方案回调函数和事件更合理更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。本篇不注重讲...

kiddddd ⋅ 06/07 ⋅ 0

JavaScript异步编程的模式

Javascript语言的执行环境是"单线程"(single thread)。所谓"单线程",就是指一次只能完成一件任务。如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务,以此类推。 这种模...

-wangming- ⋅ 2015/07/03 ⋅ 0

前端工程师必知之Promise的实现

Promise是什么? 在Javascript的世界中,代码都是单线程执行的。这就导致我们的代码里会有嵌套回调函数,一旦嵌套过多,导致代码不容易理解和维护。 为了降低异步编程的复杂性,开发人员一直...

xuerensusu ⋅ 05/18 ⋅ 0

JavaScript 异步

JavaScript怎么使用循环代替(异步)递归 问题描述 在开发过程中,遇到一个需求:在系统初始化时通过http获取一个第三方服务器端的列表,第三方服务器提供了一个接口,可通过分页形式获取列表。...

掘金官方 ⋅ 01/02 ⋅ 0

ECMAScript 6 promises(下):谈谈 API(二)

原文地址: http://www.2ality.com/2014/10/es6-promises-api.html 原文作者:Dr. Axel Rauschmayer 译者:倪颖峰 原博客已经标明:本博客文档已经过时,可进一步阅读“Exploring ES6”中的 ...

一配 ⋅ 2015/12/02 ⋅ 0

Task.js

task.js 是一个用于 ES6 的体验库,实现了顺序的、简单优化的堵塞机制,使用 JavaScript 新的 yield 操作符。 任务如同线程般交错,但它们是合作的而不是先发制人,示例代码: spawn(functi...

匿名 ⋅ 2013/04/25 ⋅ 0

ES6学习路线图

第一部分:简介 【探秘ES6】系列专栏(一):ES6简介 http://my.oschina.net/1pei/blog/520581 第二部分:let和const, Symbols, 解构赋值, 模版字符串 说明:这一部分各专题之间都比较独立,...

一配 ⋅ 2016/01/18 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

用ZBLOG2.3博客写读书笔记网站能创造今日头条的辉煌吗?

最近两年,著名的自媒体网站今日头条可以说是火得一塌糊涂,虽然从目前来看也遇到了一点瓶颈,毕竟发展到了一定的规模,继续增长就更加难了,但如今的今日头条规模和流量已经非常大了。 我们...

原创小博客 ⋅ 今天 ⋅ 0

MyBatis四大核心概念

本文讲解 MyBatis 四大核心概念(SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession、Mapper)。 MyBatis 作为互联网数据库映射工具界的“上古神器”,训有四大“神兽”,谓之:Sql...

waylau ⋅ 今天 ⋅ 0

以太坊java开发包web3j简介

web3j(org.web3j)是Java版本的以太坊JSON RPC接口协议封装实现,如果需要将你的Java应用或安卓应用接入以太坊,或者希望用java开发一个钱包应用,那么用web3j就对了。 web3j的功能相当完整...

汇智网教程 ⋅ 今天 ⋅ 0

2个线程交替打印100以内的数字

重点提示: 线程的本质上只是一个壳子,真正的逻辑其实在“竞态条件”中。 举个例子,比如本题中的打印,那么在竞态条件中,我只需要一个方法即可; 假如我的需求是2个线程,一个+1,一个-1,...

Germmy ⋅ 今天 ⋅ 0

Springboot2 之 Spring Data Redis 实现消息队列——发布/订阅模式

一般来说,消息队列有两种场景,一种是发布者订阅者模式,一种是生产者消费者模式,这里利用redis消息“发布/订阅”来简单实现订阅者模式。 实现之前先过过 redis 发布订阅的一些基础概念和操...

Simonton ⋅ 今天 ⋅ 0

error:Could not find gradle

一.更新Android Studio后打开Project,报如下错误: Error: Could not find com.android.tools.build:gradle:2.2.1. Searched in the following locations: file:/D:/software/android/andro......

Yao--靠自己 ⋅ 昨天 ⋅ 0

Spring boot 项目打包及引入本地jar包

Spring Boot 项目打包以及引入本地Jar包 [TOC] 上篇文章提到 Maven 项目添加本地jar包的三种方式 ,本篇文章记录下在实际项目中的应用。 spring boot 打包方式 我们知道,传统应用可以将程序...

Os_yxguang ⋅ 昨天 ⋅ 0

常见数据结构(二)-树(二叉树,红黑树,B树)

本文介绍数据结构中几种常见的树:二分查找树,2-3树,红黑树,B树 写在前面 本文所有图片均截图自coursera上普林斯顿的课程《Algorithms, Part I》中的Slides 相关命题的证明可参考《算法(第...

浮躁的码农 ⋅ 昨天 ⋅ 0

android -------- 混淆打包报错 (warning - InnerClass ...)

最近做Android混淆打包遇到一些问题,Android Sdutio 3.1 版本打包的 错误如下: Android studio warning - InnerClass annotations are missing corresponding EnclosingMember annotation......

切切歆语 ⋅ 昨天 ⋅ 0

eclipse酷炫大法之设置主题、皮肤

eclipse酷炫大法 目前两款不错的eclipse 1.系统设置 Window->Preferences->General->Appearance 2.Eclipse Marketplace下载【推荐】 Help->Eclipse Marketplace->搜索‘theme’进行安装 比如......

anlve ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部