封装自己的Promise

在开发中,不可避免地会使用到异步操作。想要很好地去开发异步相关的场景,至少要了解Promise的特性。而实践是在学习过程中最让人印象深刻的方法。

想要自己封装一个Promise,需要了解一下一个问题:

  1. Promise是什么;
  2. 它的出现是为了解决什么问题;
  3. 为了解决这个问题,它应该达到什么目标;
  4. 为了达到这个目标,它内部是如何封装的。

1 Promise是什么?

这里参考了MDN给出的解释:

Promise是一个对象,它代表了一个异步操作的最终完成或者失败。

2 Promise的出现是为了解决什么问题?

在没有Promise的时候,要想做多重的异步操作,会导致经典的回调地狱:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
doSomething(function(result) {

doSomething1();
doSomething2();
doSomething3();

doSomethingElse(result, function(newResult) {

doSomething4();
doSomething5();

doThirdThing(newResult, function(finalResult) {

console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);

对于回调地狱不太清楚的同学,可以参考这篇文章

回调地狱不仅是影响了代码的美观度。上面的代码中,这些“doSomething”有些是异步的,有些是同步。你要不停的思考他们的执行顺序,并且还要记在脑袋里面。这就是异步的嵌套带来的可读性的问题,它是由异步的运行机制引起的。除此之外,还有第三方接口不可靠的的问题。

简单来说,在没有Promise之前,对于异步操作中代码的执行顺序取决于请求结果什么时候返回,而有了Promise,则可以开发者自己控制代码的执行的顺序。这也是Promise要解决的问题。

3.为了解决这个问题,它应该达到什么目标?

为了在开发异步操作场景的时候,不出现回调地狱,Promise应该有自己的一套独特的机制去控制异步操作的执行顺序以及代码的可读性。

4. 为了达到这个目标,它内部是如何封装的?

这是本篇文章关注的重点。Promises/A+详细描述了Promise是怎么封装的。我们可以根据其中的描述去封装自己的Promise。

1.封装异步操作状态

首先,在要求中说到:

任何promise实例必须是一下三种状态之一: pending(待定的), fulfilled(成功的), rejected(被拒绝的).

  1. 任何promise实例处于pending状态时, 只能转换为fulfilled或者rejected状态。
  2. 任何promise实例处于fulfilled状态时:
    2.1 不能转换为其他任何一个状态(终态)。
    2.2 可能会有一个返回值,这个返回值不能被修改。
  3. 任何promise实例处于rejected状态时:
    3.1 不能转换为其他任何一个状态(终态)。
    2.2 可能会有一个返回值(失败原因),这个返回值不能被修改。

执行下面的代码:

1
2
3
4
let p = new Promise((resolve,reject)=>{
reject(100);
});
console.log(p);

结果如下:

可以看到promise用PromiseState字段去保存它的状态,用PromiseResult去保存对应结果的返回值。

Promise在构建的时候会传入一个函数1,函数1会有两个参数,一个参数是resolve函数,第二个参数是reject函数。在函数1执行的过程中:

  1. 如果调用了resolve函数,resolve函数执行完之后,promise实例的状态变PromiseState为fulfilled,同时将resolve函数的参数保存在PromiseResult中。
  2. 如果调用了reject函数,reject函数执行完之后,promise实例的状态变PromiseState为rejected,同时将resolve函数的参数保存在PromiseResult中,并抛出错误和错误原因PromiseResult。

接下来试着自己去完成上述的功能。
代码如下:

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
class MyPromise{
constructor(f){
if(typeof f !== 'function') {
throw('Uncaught TypeError: Promise resolver undefined is not a function');
}
this.PromiseState = 'pending';
this.PromiseResult = undefined;
f(this.resolve.bind(this),this.reject.bind(this));
}
resolve(res){
if(this.PromiseState === 'pending') {
this.PromiseState = 'fulfilled';
this.PromiseResult = res;
}
}
reject(res){
if(this.PromiseState === 'pending') {
this.PromiseState = 'rejected';
this.PromiseResult = res;
throw(res);
}
}
}

let mp = new MyPromise((resolve,reject)=>{
resolve(1200);
});
console.log('mp',mp);

运行结果如下:

到目前为止,自己封装的Promise实现了异步操作结果的状态保存和返回值保存。但是这远远不够,当有多个异步操作时,目前的代码完全不能处理回调地狱的问题。我们需要去实现then方法,这也是Promise最灵魂的地方。

2.封装then方法(链式调用)

看看Promises/A+是怎样描述的:

任何一个promise必须提供一个可以获取到当前或者最终返回值的then方法。
then方法接收两个参数:
promise.then(onFulfilled, onRejected)

  1. onFulfilled 和 onRejected 都是可选参数:
    1.1 如果onFulfilled不是函数,它将会被忽略
    1.2 如果onRejected不是函数,它将会被忽略
  2. 如果 onFulfilled 是函数:
    2.1 当promise的状态变为fulfilled时,onFulfilled函数必须被调用,它的参数是promised调用成功的返回值。
    2.2 在promise的状态变为fulfilled之前,onFulfilled函数必须禁止被调用。
    2.3 onFulfilled函数有且仅被调用一次。
  3. 如果 onRejected 是函数:
    3.1 当promise的状态变为rejected时,onRejected函数必须被调用,它的参数是promised调用成功的返回值。
    3.2 在promise的状态变为fulfilled之前,onRejected函数必须禁止被调用。
    3.3 onRejected函数有且仅被调用一次。
  4. 只有执行上下文栈只包含当前环境代码时,onFulfilled 或者 onRejected 函数才会被调用。
  5. onFulfilled 或者 onRejected 必须作为函数被调用。
  6. then 可以被同一个promise多次调用:
    6.1 当promise变为fulfilled状态时,每个then方法的onFulfilled应该按照then方法调用的顺序去调用。
    6.2 当promise变为rejected状态时,每个then方法的onRejected应该按照then方法调用的顺序去调用。
  7. then方法必须返回promise
    promise2 = promise1.then(onFulfilled, onRejected);
    7.1 只要promise1的onFulfilled或者onRejected返回值x,那么promise2执行resolve(x);
    7.2 只要promise1的onFulfilled或者onRejected抛出错误e,那么promise2执行reject(e);
    7.3 如果onFulfilled不是一个函数,promise1是fulfilled状态,那么promise2也会是fulfilled的状态,并且返回值和promise的返回值一样(传递性)
    7.4 如果onRejected不是一个函数,promise1是rejectedd状态,那么promise2也会是rejected的状态,并且返回值和promise的返回值一样(传递性)

根据上面的规则,尝试自己去写代码。
思路:
根据返回promise的三种状态去处理。

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
class MyPromise{
constructor(f){
if(typeof f !== 'function') {
throw('Uncaught TypeError: Promise resolver undefined is not a function');
}
this.PromiseState = 'pending';
this.PromiseResult = undefined;
this.onFulfilledQueue=[];
this.onRejectedQueue=[];
f(this.resolve.bind(this),this.reject.bind(this));
}
resolve(res){
if(this.PromiseState === 'pending') {
this.PromiseState = 'fulfilled';
this.PromiseResult = res;
this.onFulfilledQueue.forEach(f=>f());
}
}
reject(res){
if(this.PromiseState === 'pending') {
this.PromiseState = 'rejected';
this.PromiseResult = res;
this.onRejectedQueue.forEach(f=>f());
}

}
then(onFulfilled,onRejected){
self = this;
// 完成传递性
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (val)=>val;
onRejected = typeof onFulfilled === 'function' ? onRejected : (val)=>val;

// then方法返回promise2
let promise2 = new MyPromise((resolve,reject)=>{
// 当promise1的状态是pending时,将函数依次入队
if(self.PromiseState === 'pending') {
self.onFulfilledQueue.push(()=>{
try{
let x = onFulfilled(self.PromiseResult);
resolve(x);
}catch(e){
reject(e);
}
});
self.onRejectedQueue.push(()=>{
try{
let x = onRejected(self.PromiseResult);
resolve(x);
}catch(e){
reject(e);
}
});
}
// 当promise1的状态是fulfilled时,执行onFulfilled函数
if(self.PromiseState === 'fulfilled') {
try{
let x = onFulfilled(self.PromiseResult);
resolve(x);
}catch(e){
reject(e);
}

}
// 当promise1的状态是rejected时,执行onRejecteded函数
if (self.PromiseState === 'rejected') {
try{
let x = onRejected(self.PromiseResult);
resolve(x);
}catch(e){
reject(e);
}
}
});
return promise2;
}
}

let mp = new MyPromise((resolve,reject)=>{
setTimeout(()=>{
console.log('10s');
reject('can not');
},10000);
});
let mp2 = mp.then((val)=>{
return val + 3;
},(reason)=>{
console.log('mp2 onRjected',reason);
return 'this' + reason;
});
let mp3 = mp.then(()=>{

},(reason)=>{
console.log('mp3 onRejected', reason);
})
console.log('mp',mp);
console.log('mp2', mp2);
console.log('mp3',mp3);

运行结果如下:

到目前为止,我们完成了对then方法的部分封装,考虑一下,如果执行完onFulfilled函数或者onRejected函数之后的返回值是一个promise或者是promise2本身呢?这应该怎么处理?

看看Promises/A+是怎样描述的:

promise的解析程序是一个将promise和值作为输入的抽象操作,将其表示为:
[[Resolve]](promise2, x)
如果x是一个带有then方法的对象,这个操作将尝试将x的状态赋给promise。
否则,就将promise的状态变为fulfilled并且将值x作为resolve的参数。
对 thenables 的这种处理方式允许promise的实现进行互操作,只要它们暴露了一个符合 Promises/A+ 的then方法。它还允许 Promises/A+ 实现以合理的then方法”同化”不符合要求的实现。

备注(这里的x指的是执行完onFulfilled函数或者onRejected函数之后的返回值,promise2指的是then方法返回的promise2)

按照下述步骤去编写[[Resolve]](promise2, x):

  1. 如果promise和x是同一个对象,执行reject(‘TypeError’)函数;(避免死循环)
  2. 如果x是一个promise,将x的状态赋值给2:
    2.1 如果x是pending状态,promise2也必须是pending状态,直到x进入fulfilled状态或者rejected状态。
    2.2 如果x是fulfilled状态,promise2执行resolve(x.PromiseResult);
    2.3 如果x是rejected状态,promise2执行reject(x.PromiseResult);
  3. 如果x是一个对象或者函数:
    3.1 let then = x.then
    3.2 如果3.1这个操作报错了,那么就reject(报错原因e)
    3.3 如果3.1没有报错,且then是一个函数,那么调用x.then(resolvePromise,rejectPromise):
     3.3.1 resolvePromise函数里面这样写:resolvePromise(y){Resolve(promise2,y)}
     3.3.2 rejectPromise函数里面这样写:rejectPromise(e){reject(e)}
     3.3.3 如果同时调用 resolvePromise 和 rejectPromise,或者对同一参数进行多次调用,第一次调用优先,任何进一步的调用都会被忽略。
     3.3.4 如果调用then的时候报错:
         3.3.4.1 如果resolvePromise或者rejectPromise已经被调用了,忽视这个错误
         3.3.4.2 否则执行reject(报错原因e);
    
  4. 如果x不是对象或者方法,就执行resolve(x);

尝试按照上述规则去编写自己的Resolve函数。
代码如下:

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
class MyPromise{
constructor(f){
if(typeof f !== 'function') {
throw('Uncaught TypeError: Promise resolver undefined is not a function');
}
this.PromiseState = 'pending';
this.PromiseResult = undefined;
this.onFulfilledQueue=[];
this.onRejectedQueue=[];
f(this.resolve.bind(this),this.reject.bind(this));
}
resolve(res){
if(this.PromiseState === 'pending') {
this.PromiseState = 'fulfilled';
this.PromiseResult = res;
this.onFulfilledQueue.forEach(f=>f());
}
}
reject(res){
if(this.PromiseState === 'pending') {
this.PromiseState = 'rejected';
this.PromiseResult = res;
this.onRejectedQueue.forEach(f=>f());
}

}
then(onFulfilled,onRejected){
self = this;
// 完成传递性
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (val)=>val;
onRejected = typeof onFulfilled === 'function' ? onRejected : (val)=>val;

// Resolve函数,用于处理onFulfilled,onRejected函数的返回值
function Resolve(promise2,x,resolve,reject) {
// 按照promiseA+的分类情况去编写
if(x===promise2){
throw 'TypeError';
}
if(x instanceof MyPromise){
if(x.PromiseState==='fulfilled'){
resolve(x.PromiseResult);
}
if(x.PromiseState==='rejected'){
reject(x.PromiseResult);
}
if(x==='pending'){
x.onFulfilledQueue.push(()=>{
try{
let x = onFulfilled(self.PromiseResult);
resolve(x);
}catch(e){
reject(e);
}
});
x.onRejectedQueue.push(()=>{
try{
let x = onRejected(self.PromiseResult);
resolve(x);
}catch(e){
reject(e);
}
});
}
} else if(typeof x === 'object' || typeof x === 'function'){
try{
let then = x.then;
if(typeof then === 'function'){
then.call(x,(val)=>{
Resolve(promise2,val,resolve,reject);
},(res)=>{
reject(res);
})
}
}catch(e){
reject(e);
}
} else{
resolve(x);
}

}
let promise2 = new MyPromise((resolve,reject)=>{
if(self.PromiseState === 'pending') {
self.onFulfilledQueue.push(()=>{
try{
let x = onFulfilled(self.PromiseResult);
Resolve(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
});
self.onRejectedQueue.push(()=>{
try{
let x = onRejected(self.PromiseResult);
Resolve(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
});

}
if(self.PromiseState === 'fulfilled') {
try{
let x = onFulfilled(self.PromiseResult);
Resolve(promise2,x,resolve,reject);
}catch(e){
reject(e);
}

}
if (self.PromiseState === 'rejected') {
try{
let x = onRejected(self.PromiseResult);
Resolve(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
}
});
return promise2;
}
}

let mp = new MyPromise((resolve,reject)=>{
setTimeout(()=>{
console.log('5s');
reject('can not');
},5000);
});
let mp2 = mp.then((val)=>{
return val + 3;
},(reason)=>{
console.log('mp2 onRjected',reason);
return new Promise((resolve,reject)=>{
resolve(555);
});
});
let mp3 = mp.then(()=>{

},(reason)=>{
console.log('mp3 onRejected', reason);
return mp3;
})
console.log('mp',mp);
console.log('mp2', mp2);
console.log('mp3',mp3);

运行结果如下:

到这里then方法就已经封装好了,可以自己消化一下。
但是这只是promise的基本封装。
下一小节会介绍promise常用方法的封装。

打赏
  • © 2025 Aoxue
  • Hexo Theme Ayer by shenyu
    • PV:
    • UV:

请我喝杯咖啡吧~

支付宝
微信