Promise/A+ 规范与 Promise 实现代码

Promise

promise 表示一个异步操作的最终结果。和一个 promise 进行交互的主要方式是通过它的 then 方法,该方法注册回调要么接收一个 promise 的最终值,要么接收 promise 为什么不能被满足的原因。

规范

Promise A+ 规范

  1. 术语

    • promise 是一个带有 then 方法的对象或函数,其行为符合此规范。
    • thenable 是一个定义了 then 方法的对象或函数。
    • value 是任何合法的 JavaScript 值(包括undefinedthenablepromise)。
    • exception 是使用 throw 语句抛出的值。
    • reason 是一个值,它指示为什么一个承诺被拒绝。
  2. 要求

    • promise状态

      • pending:等待状态

        1. 当处于等待状态时可以转化成 fulfilled / rejected
      • fulfilled:完成状态

        1. 一定不能转换成其他状态
        2. 必须有一个不能转换的值(value)
      • rejected:拒绝状态

        1. 一定不能转换成其他状态
        2. 必须又一个不能转换的原因(reason)
    • then 方法:一个 promise 必须提供一个 then 方法来访问它的当前或最终值(value)或原因(reason),then 方法接收两个参数 promise.then(onFulfilled, onRejected)

      • onFulfilledonRejected 都是可选的参数

        1. 如果 onFulfilled 不是个函数,必须被忽略
        2. 如果 onRejected 不是个函数,必须被忽略
      • 如果 onFulfilled 是一个函数

        1. 必须在 promise 完成(resolve)后被调用,promise 的值(value)作为它第一个参数
        2. 一定不能在 promise 完成(resolve)前被调用
        3. 一定不能被调用多次
      • 如果 onRejected 是一个函数

        1. 必须在 promise 拒绝(reject)后被调用,promise 的值(reason)作为它第一个参数
        2. 一定不能在 promise 拒绝(reject)前被调用
        3. 一定不能被调用多次
      • 在执行上下文堆栈只包含平台代码[3.1]之前,不能调用 onfulfilledonRejected

      • onFulfilledonRejected 必须作为函数调用(没有 this 值)[3.2]

      • 同一个 promise 上的 then 可能被多次调用

        1. 如果 promise 被完成(resolve),所有相应的 onFulfilled 需要按照 then 的顺序被顺序执行
        2. 如果 promise 被拒绝(reject),所有相应的 onRejected 需要按照 then 的顺序被顺序执行
      • then 必须返回一个 promise 实例 [3.3] promise2 = promise1.then(onFulfilled, onRejected)

        1. 如果 onFulfilledonRejected 返回一个值 x,运行 [[Resolve]](promise2, x)
        2. 如果 onFulfilledonRejected 抛出一个异常 epromise2 必须要用 e 作为理由拒绝
        3. 如果 onFulfilled 不是一个函数并且 promise1 被完成(resolve),promise2 需要返回跟 promise1 同样的值 (value)
        4. 如果 onRejected 不是一个函数并且 promise1 被拒绝(reject),promise2 需要返回跟 promise1 同样的值 (reason)
    • promise 解决程序([[Resolve]][[Resolve]] 是一个抽象的概念,它一个 promisex 作为输入 [[Resolve]](promise, x),如果 x 是一个 thenable,它试图让承诺采用 x 的状态,假设 x 至少表现得有点像一个promise。否则,它将使用值x来实现 promise,这种 thenable 的处理允许 promise 的实现更具有通用型,只要它们暴露一个遵守 Promise/A+then 方法即可。它还允许遵守Promise/A+规范的实现可以与那些不太规范但是可用的实现进行共存,运行[[Resolve]](promise, x),执行以下步骤

      • 如果 promisex 指向同一个对象,则以 TypeError 作为原因拒绝 promise

      • 如果 x 是一个 promise 则采取它的状态 [3.4]

        1. 如果 x 状态是 pending,则 promise 必须持续 pending,直到 x 被完成或拒绝。
        2. 如果 x 状态是 resolve,则用相同的值(value)解决 promise
        3. 如果 x 状态是 reject,则用相同的原因(reason)拒绝 promise
      • 如果 x 是一个函数或者对象

        1. 使 then 的值为 x.then [3.5]
        2. 如果获取 x.then 的值抛出异常 e, 则将 e 作为原因拒绝 promise
        3. 如果 then 是一个函数,用 x 作为 this 调用它,第一个参数resolvePromise ,第二个参数 rejectPromise ,其中
          1. 如果当 resolvePromise 的值为 y 时,运行 [[Resolve]](promise, y)
          2. 如果 rejectPromise 用一个原因 r 调用,用 r 拒绝 promise
          3. 如果同时调用了 resolvePromiserejectPromise ,或者多次调用相同的参数,那么第一次调用优先,后续的调用将被忽略。(针对 thenable )
          4. 如果调用 then 抛出一个异常 e
            1. 如果 resolvePromiserejectPromise 已经被调用,忽略它。
            2. 否则用 e 作为原因拒绝 promise
        4. 如果 then 不是一个函数,用 x 完成(resolve) promise
      • 如果 x 不是一个对象或函数,用 x 完成(resolve) promise

如果一个 promisethenable 解析,并参与一个循环的 thenable 链,这样 [[Resolve]](promise, thenable) 的递归性质最终导致 [[Resolve]](promise, thenable) 再次被调用,按照上述算法将导致无限递归。我们鼓励(但不是必需)实现检测这种递归,并以 TypeError 作为原因拒绝承诺。[3.6]

  1. 注解

    1. 这里 平台代码 使引擎、环境以及promise的实现代码。在实践中,这需要确保 onFulfilledonRejected 异步地执行,并且应该在 then 方法被调用的那一轮事件循环之后用新的执行栈执行。这可以用如 setTimeoutsetImmediate 这样的“宏任务”机制实现,或者用如 MutationObserverprocess.nextTick 这样的“微任务”机制实现。由于 promise 的实现被考虑为 平台代码 ,因此在自身处理程序被调用时可能已经包含一个任务调度队列

    2. 严格模式下,它们中的this将会是undefined;在非严格模式,this将会是全局对象

    3. 假如实现满足所有需求,可以允许 promise2 === promise1 。每一个实现都应该记录是否能够产生 promise2 === promise1 以及什么情况下会出现 promise2 === promise1

    4. 通常,只有 x 来自于当前实现,才知道它是一个真正的 promise 。这条规则允许那些特例实现采用符合已知要求的 Promise 的状态

    5. 这个程序首先存储 x.then 的引用,之后测试那个引用,然后再调用那个引用,这样避免了多次访问 x.then 属性。此类预防措施对于确保访问者属性的一致性非常重要,因为访问者属性的值可能在俩次检索之间发生变化

    6. 实现不应该在 thenable 链的深度上做任意限制,并且假设超过那个任意限制将会无限递归。只有真正的循环才应该引发一个 TypeError ;如果遇到一个无限循环的 thenable,永远执行递归是正确的行为

大纲图

放张图比较好理解

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// ES6
// 三种状态
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
// 状态: 初始状态为 pending
status = PENDING
// 值
value = null
// 原因
reason = null
// 执行 onFulfilled 的队列
onFulfilledCallbacks = []
// 执行 onRejected 的队列
onRejectedCallbacks = []
// 构造方法
constructor(executor){
try {
executor(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}
resolve = value => {
// 判断是否状态处于等待状态
if(this.status === PENDING){
// 改变状态
this.status = FULFILLED
// 赋值
this.value = value
// 循环调用
while(this.onFulfilledCallbacks.length){
this.onFulfilledCallbacks.shift()(this.value)
}
}
}
reject = reason => {
// 判断是否状态处于等待状态
if(this.status === PENDING){
// 更改状态
this.status = REJECTED
// 赋值原因
this.reason = reason
// 循环调用
while(this.onRejectedCallbacks.length){
this.onRejectedCallbacks.shift()(this.reason)
}
}
}
then(onFulfilled, onRejected){
// 可选参数
const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
const realOnRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason }
const promise1 = new MyPromise((resolve, reject) => {
// 创建一个微任务执行完成的函数
const fulfilledMicrotask = () => {
queueMicrotask(() => {
try{
let x = realOnFulfilled(this.value)
resolvePromise(promise1, x, resolve, reject)
}catch (error) {
reject(error)
}
})
}
// 创建一个微任务执行拒绝的函数
const rejectMicrotask = () => {
queueMicrotask(() => {
try{
let x = realOnRejected(this.reason)
resolvePromise(promise1, x, resolve, reject)
}catch (error) {
reject(error)
}
})
}
// 状态确定后直接执行
if(this.status == FULFILLED){
fulfilledMicrotask()
}else if(this.status == REJECTED){
rejectMicrotask()
}else{
// 异步,加入队列
this.onFulfilledCallbacks.push(fulfilledMicrotask)
this.onRejectedCallbacks.push(rejectMicrotask)
}
})
// then 返回一个新的 promise
return promise1
}
// catch 方法
catch (onRejected) {
this.then(null, onRejected)
}
}

function resolvePromise(promise, x, resolve, reject){
if(x === promise){
// 循环调用,直接报错
return reject(new TypeError('The promise and the return value are the same'));
}
if(typeof x === 'function' || typeof x === 'object'){
// null 直接返回
if(x === null) return resolve(x)

let then
try {
then = x.then
} catch (error) {
// 不存在直接拒绝
return reject(error)
}
// 如果对象上面存在 then 方法
if(typeof then === 'function'){
let called = false
try {
then.call(x, y => {
// 执行多次忽略
if(called) return
called = true
// 接着执行
resolvePromise(promise, y, resolve, reject)
}, r => {
// 执行多次忽略
if(called) return
called = true
reject(r)
})
} catch (error) {
//
if(called) return
called = true
reject(error)
}
}else{
// then 不是函数
resolve(x)
}
}else{
// 如果 x 不为对象或者函数,直接用 x 为参数执行 promise
resolve(x)
}
}

// 测试用
MyPromise.deferred = function(){
var result = {}
result.promise = new MyPromise(function(resolve, reject){
result.resolve = resolve
result.reject = reject
})
return result
}

module.exports = MyPromise
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
// ES5
var PENDING = 'pending';
var REJECTED = 'rejected';
var FULFILLED = 'fulfilled';
function MyPromise(executor){
this.status = PENDING
this.value = null;
this.reason = null;
this.onFulfilled = []
this.onRejected = []
this.resolve = this.resolve.bind(this)
this.reject = this.reject.bind(this)
try {
executor(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}
MyPromise.prototype.resolve = function(value){
if(this.status === PENDING){
this.status = FULFILLED
this.value = value
while(this.onFulfilled.length){
this.onFulfilled.shift()(this.value)
}
}
}
MyPromise.prototype.reject = function(reason){
if(this.status === PENDING){
this.status = REJECTED
this.reason = reason
while(this.onRejected.length){
this.onRejected.shift()(this.reason)
}
}
}
MyPromise.prototype.then = function(onFulfilled, onRejected){
var that = this
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : function(value){ return value }
onRejected = typeof onRejected === 'function' ? onRejected : function(reason){ throw reason }
var promise1 = new MyPromise(function(resolve, reject){
var fulfilled = function(){
// 没有 queueMicrotask 可以用 setTimeout
queueMicrotask(function(){
try {
var x = onFulfilled(that.value)
resolvePromise(promise1, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
var rejected = function(){
queueMicrotask(function(){
try {
var x = onRejected(that.reason)
resolvePromise(promise1, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
if(that.status === FULFILLED){
fulfilled()
}else if(that.status === REJECTED){
rejected()
}else{
that.onFulfilled.push(fulfilled)
that.onRejected.push(rejected)
}
})
return promise1
}
MyPromise.prototype.catch = function(onRejected){
this.then(null, onRejected)
}

执行流程

image.png

参考文章

从一道让我失眠的 Promise 面试题开始,深入分析 Promise 实现细节