文章类型:个人学习笔记
手写 Promise 的前提是了解 Promise/A+ 规范,不说倒背如流,但是基本的点还是需要掌握的。
1.Promise/A+规范
原文地址:Promise/A+规范
提醒:翻译是采用个人理解+谷歌翻译逐字逐句翻译
(1)术语:
1.1. “promise” 是一个带有符合此行为规范 then 方法的对象(object)或函数(function)。
1.2. “thenable” 是定义 then 方法的一个对象(object)或函数(function)。
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. 当 promise 为 pending 状态时:
- 2.1.1.1. 可能会转换为 fulFilled状态 或 rejected状态
2.1.2. 当 promise 为 fulFilled 状态时:
2.1.3. 当 promise 为 rejected 状态时:
在此,“不能更改”意味着恒等不变(即:===),但并不表示更深层次的不可改变。
2.2. then方法
一个 Promise 必须提供一个 then 方法用来访问 当前值、最终的 value值 或 reason。
一个 Promise 的 then 方法接收两个参数:
1
|
promise.then(onFulfilled, onRejected)
|
2.2.1. onFulfilled 和 onRejected 都是可选参数:
2.2.2. 如果 onFulfilled 是一个函数:
2.2.3. 如果 onRejected 是一个函数:
2.2.4. onFulfilled 或 onRejected 不能在执行上下文堆栈仅包含平台代码之前调用。[3.1]
2.2.5. onFulfilled 和 onRejected 必须作为函数调用(即 没有 this 值)。[3.2]
2.2.6. then 可能在同一个 promise 中被多次调用。
2.2.7. then 必须返回一个 promise [3.3] 。
1
|
promise2 = promise1.then(onFulfilled, onRejected);
|
- 2.2.7.1. 如果 onFulfilled 或 onRejected 返回一个 value x,运行 promise 解决程序:
1
|
[[Resolve]](promise2, x)
|
-
2.2.7.2. 如果 onFulfilled 或 onRejected 抛出异常 e ,promise2 必须执行 rejected 将 e 作为 reason 的值。
-
2.2.7.3. 如果 onFulfilled 不是一个函数并且 promise1 是 fulFilled 状态,promise2 必须是 fulFilled 状态且带着 promise1 同样的值。
-
2.2.7.4. 如果 onRejected 不是一个函数并且 promise1 是 rejected 状态,promise2 必须是 rejected 状态且带着 promise1 同样的 reason。
2.3. Promise 执行程序 (解决程序)
Promise 解决程序是一个抽象操作表现为输入一个 promise 和一个 value值,我们用 [[Resolve]](promise, x) 表示,如果 x 是一个 thenable,它试图使 promise 采用 x 的状态,假设 x 的行为和 promise 类似。否则,它带着一个 value x 使 promise 转为 fulfilled 状态。
这种对于 thenable 的处理就允许 promise 实现互操作,只要他们暴露一个符合 Promises/A+ 的 then 方法。它也允许Promises/A+ 实现使用合理的 then 方法“整合”不合格的实现。
要运行 [[Resolve]](promise,x),请执行以下步骤:
2.3.1. 如果 promise 和 x 指向同一个对象,promise 带着一个 TypeError 作为 reason 执行 reject。
2.3.2. 如果 x 是一个 promise ,采用它的状态 [3.4] :
-
2.3.2.1。 如果 x 是 pending 状态,promise 必须保持 pending 状态直到 x 是 fulFilled 状态或 rejected 状态。
-
2.3.2.2. 如果当 x 是 fulfilled 状态,带着同样的 value 让 promise 变成 fulFilled 状态。
-
2.3.2.3. 如果当 x 是 rejected 状态,带着同样的 reason 让 promise 变成 rejected 状态。
2.3.3. 除此之外,如果 x 是一个对象或函数:
-
2.3.3.1. 将 x.then 赋值给 then。[3.5]
-
2.3.3.2. 如果使用属性 x.then 引发抛出异常 e,带着 e 作为 reason 让 promise 变成 rejected 状态。
-
2.3.3.3. 如果 then 是一个函数,用 x 作为 this 调用它,第一个参数 resolvePromise,第二个参数 rejectPromise,如下:
-
- 2.3.3.3.1. 如果当 resolvePromise 带着一个 value y 被调用,执行 [[Resolve]](promise, y)。
-
- 2.3.3.3.2. 如果当 rejectPromise 带着一个 reason r 被调用,带着 r 作为 reason 让 promise 变成 rejected 状态。
-
- 2.3.3.3.3. 如果同时调用 resolvePromise 和 rejectPromise ,或者对同一参数进行了多次调用,则第一个调用优先,而所有其他调用均被忽略。
-
- 2.3.3.3.4. 如果调用 then 抛出异常 e:
-
-
- 2.3.3.3.4.1. 如果 resolvePromise 或 rejectPromise已经被调用,请忽略它。
-
-
- 2.3.3.3.4.2. 否则,带着 e 作为 reason 让 promise 变成 rejected 状态。
2.3.4. 如果 then 不是一个函数,带着 x 作为 value 让 promise 变成 fulFilled 状态。
如果一个 Promise 是 resolved 带着一个 thenable 在一个 thenable 循环链中,这样 [[Resolve]](promise, thenable) 的递归性质最终导致 [[Resolve]](promise, thenable) 再次被调用,遵循上述算法将导致无限递归,鼓励但不是必需的实现,以检测此类递归并以信息 TypeError 为 reason 的执行 promise 的 reject。[3.6]
(3)附注
3.2. 也就是说,在严格模式下,this 是 undefined 。 在非严格模式下,this 是全局对象。
3.3. 如果实现满足所有要求,则实现可以允许 promise2 === promise1 。 每个实现都应记录是否可以产生 promise2 === promise1 以及在什么条件下产生。
3.4. 通常,只有 x 来自当前的实现,才知道它是一个真正的 promise 。 本节允许使用特定的实现的方式来采用已知符合 promise 的状态。
3.5. 首先存储对 x.then 的引用,然后测试该引用,然后调用该引用的过程避免了对 x.then 属性的多次访问。 此类预防措施对于确保访问者属性的一致性非常重要,因为访问者属性的值在两次检索之间可能会发生变化。
3.6. 实现不应在可建立链的深度上设置任意限制,并假定超出该任意限制,则递归将是无限的。 只有真正的循环才应该导致 TypeError 。 如果遇到无限多个不同的罐头,则永远递归是正确的行为。
(3)总结
2.对照PromiseA+规范手写 Promise
(1)Promise 状态
1.pending:待定
2.fulFilled:已解决
3.rejected:拒绝
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
|
// 1.首先 Promise 有三个状态,初始状态为pending
// 2.当 Promise 为 fulFilled 或 rejected状态时,不能转变成其它状态
// 3.当 Promise 为 fulFilled 或 rejected状态时,
// 必须有一个值(fulFilled是value,rejected是reason)
// 4.Promise 接收一个回调函数
function MyPromise(exector) {
const PENDING = 'pending';
const FULFILLED = 'fulFilled';
const REJECTED = 'rejected';
const that = this;
that.status = PENDING;
function resolve(value) {
// 这里判断的原因就是Promise状态改为FULFILLED,不能再进行改变
if (that.status === PENDING) {
that.status = FULFILLED;
that.value = value;
}
}
function reject(reason) {
// 这里判断的原因跟上面一样
if (that.status === PENDING) {
that.status = REJECTED;
that.value = reason;
}
}
try {
exector(resolve, reject);
} catch (error) {
reject(error);
}
}
|
上述resolve和reject方法中如果不进行判断,下面这种情况就会再次改变promise的状态:
1
2
3
4
5
|
let p = new MyPromise((resolve, rejrect) => {
reject('拒绝');
resolve('解决');
})
console.log(p);
|
上面这个例子,如果不进行判断,最终状态会是 fulfilled ,进行判断后状态是 rejected ,不会再被更改,这样才是满足Promise/A+规范的。
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
|
class MyPromise {
static PENDING = 'pending';
static FULFILLED = 'fulFilled';
static REJECTED = 'rejected';
constructor(executor) {
this.status = MyPromise.PENDING;
this.value = null;
try {
// 这个地方this.resolve只是拿到resolve方法,并不是this调用的
// 事实上是全局对象调用的,但是class里面默认是严格模式
// 如果不改变resolve的内部this指向,他的this是undefined
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error)
}
}
resolve(value) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.FULFILLED;
this.value = value;
}
}
reject(reason) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED;
this.reason = reason;
}
}
}
// 测试
let p = new MyPromise(function (resolve, reject) {
reject('XKHM')
});
console.log(p);
|
2.then方法
1
|
promise.then(onFulfilled, onRejected)
|
1.then方法的两个参数onFulfilled和onRejected都是可选参数,如果这两个不是函数,将被忽略。
2.onFulfilled一定是在Promise执行完状态为fulFilled时调用,并接受一个参数value,并且最多被调用一次
3.onRejected一定是在Promise执行完状态为rejected时调用,并接受一个参数reason,并且最多被调用一次
4.onFulfilled和onRejected需要在下一轮事件循环中调用(异步调用)
5.onFulfilled和onRejected需要作为函数调用(没有自己的this值)
6.then方法可以被Promise链式调用
7.then方法必须返回一个Promise对象
提醒:
(1)then的时候需要处理Promise的三种状态,包括pending状态
(2)如果then本身在没经过处理的情况下就返回的是Promise,需要单独处理
3.Promise解决过程
Promise解决程序是一个抽象的操作,其需输入一个 promise 和一个值,我们表示为 [[Resolve]](promise, x)
1.如果Promise和x指向同一对象,以 TypeError 为据因拒绝执行 promise
2.如果 x 为 promise ,接受其状态
3.抑或 x 为对象或者函数
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
|
class MyPromise {
static PENDING = 'pending';
static FULFILLED = 'fulFilled';
static REJECTED = 'rejected';
constructor(executor) {
this.status = MyPromise.PENDING;
this.value = null;
this.callback = [];
try {
// 这个地方this.resolve只是拿到resolve方法,并不是this调用的
// 事实上是全局对象调用的,但是class里面默认是严格模式
// 如果不改变resolve的内部this指向,他的this是undefined
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error)
}
}
resolve(value) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.FULFILLED;
this.value = value;
setTimeout(_ => {
this.callback.forEach(callFn => {
callFn.onFulfilled(value)
});
})
}
}
reject(value) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED;
this.value = value;
setTimeout(_ => {
this.callback.forEach(callFn => {
callFn.onRejected(value)
});
})
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
onFulfilled = value => value;
}
if (typeof onRejected !== 'function') {
onRejected = value => value;
}
let promise = new MyPromise((resolve, reject) => {
if (this.status === MyPromise.PENDING) {
this.callback.push({
onFulfilled: value => {
this.parse(promise, onFulfilled(this.value), resolve, reject)
},
onRejected: value => {
this.parse(promise, onRejected(this.value), resolve, reject)
}
})
}
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
this.parse(promise, onFulfilled(this.value), resolve, reject)
})
}
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
this.parse(promise, onRejected(this.value), resolve, reject)
})
}
});
return promise;
}
parse(promise, res, resolve, reject) {
if (promise == res) {
throw new TypeError('then返回的promise不能是跟then是相同的Promise');
}
try {
if (res instanceof MyPromise) {
res.then(resolve, reject)
} else {
resolve(res)
}
} catch (error) {
reject(error)
}
}
}
|
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
|
class MyPromise {
static PENDING = 'pending';
static FULFILLED = 'fulFilled';
static REJECTED = 'rejected';
constructor(executor) {
this.status = MyPromise.PENDING;
this.value = null;
this.callback = [];
try {
// 这个地方this.resolve只是拿到resolve方法,并不是this调用的
// 事实上是全局对象调用的,但是class里面默认是严格模式
// 如果不改变resolve的内部this指向,他的this是undefined
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (error) {
this.reject(error)
}
}
resolve(value) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.FULFILLED;
this.value = value;
setTimeout(_ => {
this.callback.forEach(callFn => {
callFn.onFulfilled(value)
});
})
}
}
reject(value) {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED;
this.value = value;
setTimeout(_ => {
this.callback.forEach(callFn => {
callFn.onRejected(value)
});
})
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') {
onFulfilled = value => value;
}
if (typeof onRejected !== 'function') {
onRejected = value => value;
}
let promise = new MyPromise((resolve, reject) => {
if (this.status === MyPromise.PENDING) {
this.callback.push({
onFulfilled: value => {
this.parse(promise, onFulfilled(this.value), resolve, reject)
},
onRejected: value => {
this.parse(promise, onRejected(this.value), resolve, reject)
}
})
}
if (this.status === MyPromise.FULFILLED) {
setTimeout(() => {
this.parse(promise, onFulfilled(this.value), resolve, reject)
})
}
if (this.status === MyPromise.REJECTED) {
setTimeout(() => {
this.parse(promise, onRejected(this.value), resolve, reject)
})
}
});
return promise;
}
parse(promise, res, resolve, reject) {
if (promise == res) {
throw new TypeError('then返回的promise不能是跟then相同的Promise');
}
try {
if (res instanceof MyPromise) {
res.then(resolve, reject)
} else {
resolve(res)
}
} catch (error) {
reject(error)
}
}
static resolve(value) {
return new MyPromise((resolve, reject) => {
if (value instanceof MyPromise) {
value.then(resolve, reject);
} else {
resolve(value);
}
})
}
static reject(reason) {
return new MyPromise((_, reject) => {
reject(reason);
})
}
static all(promises) {
let resolves = [];
return new MyPromise((resolve, reject) => {
promises.forEach((promise, index) => {
promise.then(value => {
resolves.push(value)
if (resolves.length === promises.length) {
resolve(resolves);
}
}, reason => {
reject(reason);
})
})
})
}
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(value => {
resolve(value)
})
})
})
}
}
|
4.测试
使用 Promise/A+ 测试工具
1
|
npm install promises-aplus-tests -g
|
1
|
npx promises-aplus-tests promise.js
|
5.提醒
(1)Promise内部错误被吞掉了,那是被谁吞掉的呢,是被reject吞掉的
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
|
const pending = 'pending';
const fulFilled = 'fulFilled';
const rejected = 'rejected';
const resolveFn = [];
const rejectFn = [];
// Promise/A+规范
// 1. Promise状态
// (1) 当Promise为pending状态时,可以被转换成另外两种状态
// (2) 当Promise为fulFilled状态时,不能被更改,且必须有一个不能被更改的value值
// (3) 当Promise为rejected状态时,不能被更改,且必须有一个不能被更改的reason值
function MyPromise(execute){
const that = this;
that.status = pending;
function resolve(value){
if(that.status === pending){
that.status = fulFilled;
that.value = value;
}
}
function reject(reason){
if(that.status === pending){
that.status = rejected;
that.value = reason;
}
}
// 如果执行中有错误,会立马执行reject
// 也就是说Promise中的错误是被reject吞掉的
// try...catch是捕获不到的
try {
execute(resolve,reject);
} catch (error) {
reject(error)
}
}
// 2.then方法
// 首先,then方法是挂在Promise原型上的一个方法
// (1)then方法接收两个回调函数,如果不是函数忽略
// (2)then方法返回一个Promise
// (3)then方法需要异步执行
// (4)then可以被链式调用
// (5)如果onResolve是函数,必须在Promise状态为fulFilled后调用
// (6)如果onRejected是函数,必须在Promise状态为rejected后调用
// (7)then没有自己的this(也可以理解为then方法是挂在Promise原型上的)
MyPromise.prototype.then = function(onResolve,onRejected){
if(typeof onResolve !== 'function'){
return onResolve = value => value
}
if(typeof onRejected !== 'function'){
return onRejected = reason => reason
}
let that = this;
let p = new MyPromise((resolve,reject) => {
if(that.status === fulFilled){
setTimeout(_ => {
onResolve(that.value)
})
}
if(that.status === rejected){
setTimeout(_ => {
onRejected(that.value)
})
}
})
return p;
}
// 测试
try {
let p = new MyPromise((resolve,reject) => {
throw new TypeError('抛出错误')
})
console.log(p);
} catch (error) {
console.log(error);
}
// p.then();
try {
let p1 = new Promise(resolve => {
throw new TypeError('抛出错误')
})
console.log(p1);
} catch (error) {
console.log(error);
}
|
Promise实例调用then时,如果then回调函数里面抛出了错误,也会被reject吞掉
6.手写Promise的意义
相当于照着一个规范然后用代码区实现,相当于深入了解源码,只是Promise源码才100多行,算阅读一个小型项目的源码把。
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
|
const pending = 'pending';
const fulFilled = 'fulFilled';
const rejected = 'rejected';
// Promise/A+规范
// 1. Promise状态
// (1) 当Promise为pending状态时,可以被转换成另外两种状态
// (2) 当Promise为fulFilled状态时,不能被更改,且必须有一个不能被更改的value值
// (3) 当Promise为rejected状态时,不能被更改,且必须有一个不能被更改的reason值
function MyPromise(execute){
const that = this;
that.status = pending;
that.resolveFn = [];
that.rejectFn = [];
function resolve(value){
setTimeout(_ => {
if(that.status === pending){
that.status = fulFilled;
that.value = value;
that.resolveFn.forEach(f => {
f(that.value)
})
}
})
}
function reject(reason){
setTimeout(_ => {
if(that.status === pending){
that.status = rejected;
that.value = reason;
that.rejectFn.forEach(f => {
f(that.value)
})
}
})
}
// 如果执行中有错误,会立马执行reject
// 也就是说Promise中的错误是被reject吞掉的
// try...catch是捕获不到的
try {
execute(resolve,reject);
} catch (error) {
reject(error)
}
}
// 2.then方法
// 首先,then方法是挂在Promise原型上的一个方法
// (1)then方法接收两个回调函数,如果不是函数忽略
// (2)then方法返回一个Promise
// (3)then方法需要异步执行
// (4)then可以被链式调用
// (5)如果onResolve是函数,必须在Promise状态为fulFilled后调用
// (6)如果onRejected是函数,必须在Promise状态为rejected后调用
// (7)then没有自己的this(也可以理解为then方法是挂在Promise原型上的)
MyPromise.prototype.then = function(onResolve,onRejected){
if(typeof onResolve !== 'function'){
onResolve = value => value
}
if(typeof onRejected !== 'function'){
onRejected = reason => {
throw reason
}
}
const that = this;
let promise;
if(that.status === fulFilled){
promise = new MyPromise((resolve,reject) => {
setTimeout(_ => {
try {
let x = onResolve(that.value);
resolvePromise(promise,x,resolve,reject)
} catch (error) {
reject(error)
}
})
})
}
if(that.status === rejected){
promise = new MyPromise((resolve,reject) => {
setTimeout(_ => {
try {
let x = onRejected(that.value);
resolvePromise(promise,x,resolve,reject)
} catch (error) {
reject(error)
}
})
})
}
if(that.status === pending){
promise = new MyPromise((resolve,reject) => {
that.resolveFn.push(_ => {
try {
let x = onResolve(that.value);
resolvePromise(promise,x,resolve,reject)
} catch (error) {
reject(error)
}
})
that.rejectFn.push(_ => {
try {
let x = onRejected(that.value);
resolvePromise(promise,x,resolve,reject)
} catch (error) {
reject(error)
}
})
})
}
return promise;
}
// 3.Promise执行程序
// 1.promise和x指向同一个对象,执行reject()
// 2.x是promise,采用它的状态
// 3.x是对象或函数,处理
// 4.then不是函数
function resolvePromise(promise,x,resolve,reject){
if(promise === x){
return reject(new TypeError('promise不能和x相等'))
}
if(x instanceof MyPromise){
if(x.status === fulFilled){
resolve(x.value)
}else if(x.status === rejected){
reject(x.value)
}else{
x.then(y => {
resolvePromise(promise,y,resolve,reject)
},reject)
}
}else if(x !== null && (typeof x === 'object' || typeof x === 'function')){
let executed;
try {
let then = x.then;
if(typeof then === 'function'){
then.call(x,y => {
if(executed){
return false
}
executed = true;
resolvePromise(promise,y,resolve,reject)
},e => {
if(executed){
return false
}
executed = true;
reject(e);
})
}else{
resolve(x)
}
} catch (error) {
if(executed){
return false
}
executed = true;
reject(error);
}
}else{
resolve(x)
}
}
|