Promise的实现

前面已经写过一篇文章来理解Promise及其使用方法,这里尝试来模拟Promise

要求

根据Promise/A+原理,有以下几个点需要遵守:

  1. Promise有一个Promise States,其有三种状态:

    1. pending:初始状态,可以转化为fulfilledrejected
    2. fulfilled
      1. 需要一个value作为结果。
      2. 不能转化为其他状态。
    3. rejected
      1. 需要一个reason作为一个原因。
      2. 不能转化为其他状态。
  2. 必须要一个then方法,接受两个参数

    1. onFulfilled :当操作成功时发生的回调。
    2. onRejected :当操作失败时发生的回调。

    一旦promise完成,每一次调用then方法,得到的结果必须是相同的。

  3. Promise返回的必须也是一个promise

开发

实现1,2功能

首先我们实现第一个和第二个特点:

基本思路如下:

  • 一个Promise首先要有一个status,表示当前的状态,其有三种状态:

    1. PENDING:表示当前Promise未完成,此时的回调函数会加入相应的队列
    2. RESOLVED:表示当前Promise已经完成,且时resolve状态,直接调用成功的函数。
    3. REJECTED:表示当前Promise已经完成,且时rejected状态,直接调用失败的函数。
  • 然后我们要定义两个值:

    1. value:用于resolve时的返回值。
    2. reason:用于rejected时的返回值。
  • 上面说到当Promise状态为PENDING时,我们会把回调函数放到相应的队列,所以我们还会定义两个队列

    1. resolvedCallQueue:存放成功的回调函数
    2. rejectedCallQueue:存放失败的回调函数
  • 然后我们定义Promiseresolverejected函数,在这两个函数中,我们会改变Promise的状态并且给value或者reason赋值,最后执行相应的函数队列。

  • 最后我们会定义Promisethen方法。如同上面对于状态的定义,我们会根据当前Promise的状态来处理then方法传来的回调函数。

下面用ES6class来实现:

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
class Promise {
constructor(executer) {
//定义Promise状态枚举数据
this.status_enum = {
PENDING: 'PENDING',
RESOLVED: 'RESOLVED',
REJECTED: 'REJECTED',
};
//成功的返回值
this.value = undefined;
//失败的返回值
this.reason = undefined;
//成功的回调函数队列
this.resolvedCallQueue = [];
//失败的回调函数队列
this.rejectedCallQueue = [];
//当前Promise状态
this.status = this.status_enum.PENDING;
//Promise完成
let resolve = (value) => {
if (this.status === this.status_enum.PENDING) {
this.status = this.status_enum.RESOLVED;
this.value = value;
for (let i = 0; i < this.resolvedCallQueue.length; i++) {
this.resolvedCallQueue[i](this.value);
}
}
};
//Promise失败
let reject = (reason) => {
if (this.status === this.status_enum.PENDING) {
this.status = this.status_enum.REJECTED;
this.reason = reason;
for (let i = 0; i < rejectedCallQueue.length; i++) {
this.rejectedCallQueue[i](this.reason);
}
}
};
//执行Promise函数
try {
executer(resolve, reject);
} catch (e) {
console.log('错误', e);
reject(e);
}
}
//定义then函数
then(onfulfilled, onrejected) {
//检测参数必须为函数
if(!onfulfilled instanceof Function || !onrejected instanceof Function){
throw new Error('Uncaught TypeError: Promise resolver is not a function')
}
//当Promise状态为RESOLVED时,进行成功回调函数
if (this.status === this.status_enum.RESOLVED) {
onfulfilled(this.value);
}
//当Promise状态为REJECTED时,进行失败回调函数
if (this.status === this.status_enum.REJECTED) {
onrejected(this.reason);
}
//当Promise状态为PENDING时,将其回调函数加入相应队列,在完成时会执行相应的函数队列
if (this.status === this.status_enum.PENDING) {
this.resolvedCallQueue.push(onfulfilled);
this.rejectedCallQueue.push(onrejected);
}
}
}

下面用原型实现(基本原理一样,因为class本质也是原型链的语法糖):

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
function Promise(executer) {
this.status_enum = {
PENDING: 'PENDING',
RESOLVED: 'RESOLVED',
REJECTED: 'REJECTED',
};
this.value = undefined;
this.reason = undefined;
this.resolvedCallQueue = [];
this.rejectedCallQueue = [];
this.status = this.status_enum.PENDING;
let resolve = (value) => {
if (this.status === this.status_enum.PENDING) {
this.status = this.status_enum.RESOLVED;
this.value = value;
for (let i = 0; i < this.resolvedCallQueue.length; i++) {
this.resolvedCallQueue[i](this.value);
}
}
};
let reject = (reason) => {
if (this.status === this.status_enum.PENDING) {
this.status = this.status_enum.REJECTED;
this.reason = reason;
for (let i = 0; i < rejectedCallQueue.length; i++) {
this.rejectedCallQueue[i](this.reason);
}
}
};
try {
executer(resolve, reject);
} catch (e) {
console.log('错误', e);
reject(e);
}
}
Promise2.prototype.then = function then(onfulfilled, onrejected) {
if (this.status === this.status_enum.RESOLVED) {
onfulfilled(this.value);
}
if (this.status === this.status_enum.REJECTED) {
onrejected(this.reason);
}
if (this.status === this.status_enum.PENDING) {
this.resolvedCallQueue.push(onfulfilled);
this.rejectedCallQueue.push(onrejected);
}
};

这样,Promise的基本功能就已经实现,简单测试如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let p = new Promise((resolve, reject) => {
setTimeout(() => {
console.log('2秒!!!');
resolve(111);
}, 2000);
});
p.then((res) => {
console.log('p1');
console.log(res);
});
p.then((res) => {
console.log('p2');
console.log(res);
});

结果:

1
2
3
4
5
6
7
8
9
2秒!!!

p1

111

p2

111

实现1,2,3功能

前面我们基本实现了Promise的基本功能,但是还是一个问题是:Promise必须返回一个Promise,上面的代码并不满足这一点。

所谓我们为了实现返回的都是Promise,我们需要重写then方法,使之返回的是一个Promise,这时,我们还是要根据当前Promise的状态来分开来处理。

但是其中心点是,必须持续执行Promise,直到其返回值不是一个thenable对象或方法,所以这是一个递归的过程。所以我们定义了一个函数cycleResolve,其接受四个参数:

  1. newPromise:新建的被用于返回的Promise
  2. target:回调函数执行的获得结果(可能仍然是一个Promise
  3. resolve:新Promiseresolve函数
  4. reject:新Promisereject函数

该函数的功能是:判定返回值target是不是一个thenable对象(包括Promise),如果是,继续执行其then方法。经过这个过程,知道返回值不是thenable对象,然后将其值resolve出去。

注意:

  1. 只有target是一个thenable对象并且其then属性是一个函数时,才会调用其then方法,否则会直接将targetresolve出去。
  2. 一旦遇到错误,都会直接reject(e)
  3. then方法中必须使用setTimeout来使内部的操作成为宏任务,在下一个tick执行。这样才能拿到newPromise,否则会出现未初始化错误。
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
class Promise {
constructor(executer) {
this.status_enum = {
PENDING: 'PENDING',
RESOLVED: 'RESOLVED',
REJECTED: 'REJECTED',
};
this.value = undefined;
this.reason = undefined;
this.resolvedCallQueue = [];
this.rejectedCallQueue = [];
this.status = this.status_enum.PENDING;
//定义resolve方法,任务完成时调度
this.resolve = (value) => {
if (this.status === this.status_enum.PENDING) {
this.status = this.status_enum.RESOLVED;
this.value = value;
for (let i = 0; i < this.resolvedCallQueue.length; i++) {
this.resolvedCallQueue[i](this.value);
}
}
};
//定义reject方法,任务失败时调度
this.reject = (reason) => {
if (this.status === this.status_enum.PENDING) {
this.status = this.status_enum.REJECTED;
this.reason = reason;
for (let i = 0; i < this.rejectedCallQueue.length; i++) {
this.rejectedCallQueue[i](this.reason);
}
}
};
//执行定义Promise时传入的任务
try {
executer(this.resolve, this.reject);
} catch (e) {
console.log('错误', e);
this.reject(e);
}
}
//定义then方法
then(onfulfilled, onrejected) {
//新建一个Promise用于返回
let newPromise = new Promise((resolve, reject) => {
if (this.status === this.status_enum.RESOLVED) {
setTimeout(() => {
try {
let target = onfulfilled(this.value);
cycleResolve(newPromise, target, resolve, reject);
} catch (e) {
console.log(e);
reject(e);
}
}, 0);
}
if (this.status === this.status_enum.REJECTED) {
//建立宏任务,方便拿到newPromise,否则会出现未初始化错误
setTimeout(() => {
try {
let target = onrejected(this.reason);
cycleResolve(newPromise, target, resolve, reject);
} catch (e) {
console.log(e);
reject(e);
}
});
}
if (this.status === this.status_enum.PENDING) {
this.resolvedCallQueue.push(() => {
setTimeout(() => {
try {
let target = onfulfilled(this.value); //将resolve函数保留的成功值传递作为参数
cycleResolve(newPromise, target, resolve, reject);
} catch (e) {
console.log(e);
reject(e);
}
}, 0);
});
this.rejectedCallQueue.push(() => {
setTimeout(() => {
try {
let target = onrejected(this.reason); //将resolve函数保留的成功值传递作为参数
cycleResolve(newPromise, target, resolve, reject);
} catch (e) {
console.log(e);
reject(e);
}
}, 0);
});
}
});
return newPromise;
}

function cycleResolve(newPromise, target, resolve, reject) {
//禁止循环调用
if (newPromise === target) {
return reject(new TypeError('循环调用'));
}
if (
target !== null &&
(typeof target === 'object' ||
typeof target === 'function') /*确定其是一个对象*/
) {
try {
let then = target.then; /*确定是否是一个thenable对象*/
if (typeof then === 'function') {
then.call(
target,
(newTarget) => {
resolvePromise(newPromise, newTarget, resolve, reject);
},
(e) => {
reject(e);
}
);
} else {
resolve(target);
}
} catch (e) {
reject(e);
}
} else {
resolve(target);
}
}

至此,Promise的基本功能都已经完成,接下来我们完成剩下的一些细枝末节的东西,包括:

  1. 实现resolve方法
  2. 实现reject方法
  3. 实现catch方法。
  4. 实现finally方法。
  5. 实现Promise的其他方法:
    1. Promise.all([p1, p2, p3])
    2. Promise.race([p1, p2, p3])
    3. Promise.allSettled(p1, p2, p3)
    4. Promise.any(p1, p2, p3)

注意:

  1. 各个函数的功能不再赘述,具体查看Promise理解
  2. 下面的代码就只写对应部分,多余部分不再进行赘述。

实现catch方法

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
catch(onrejected) { /*在class中定义catch方法*/
const newPromise = new Promise3((undefined, reject) => {
if (this.status === this.status_enum.REJECTED) {
setTimeout(() => {
try {
let target = onrejected(this.reason);
cycleReject(newPromise, target, reject);
} catch (e) {
console.log(e)
reject(e);
}
}, 0);
}
if (this.status === this.status_enum.PENDING) {
this.rejectedCallQueue.push(() => {
setTimeout(() => {
try {
let target = onrejected(this.reason);
cycleReject(newPromise, target, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
}
}

//循环reject
function cycleReject(newPromise, target, reject) {
//禁止循环调用
if (newPromise === target) {
return reject(new TypeError('循环调用'));
}
if (
target !== null &&
(typeof target === 'object' ||
typeof target === 'function') /*确定其是一个对象*/
) {
try {
let then = target.then; /*确定是否是一个thenable对象*/
if (typeof then === 'function') {
then.call(
target,
(newTarget) => {
resolvePromise(newPromise, newTarget, reject);
},
(e) => {
reject(e);
}
);
}
} catch (e) {
reject(e);
}
}else{
reject(target)
}
}

其实这里的操作与then方法逻辑基本一致,唯一不同的是,我们只需要捕捉reject,不捕捉resolve

实现finally

Promisefinally的方法无论promise的结果是成功还是失败,都会执行,并且返回该promise。所以实现很简单。执行其promisethen方法来获取该promise的结果。

1
2
3
4
finally(callback) {
callback();
return this.then(() => {}, () => {});
}

实现Promise.resolve方法(下面的方法是直接作为class的静态函数成员的)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static resolve(val) {
//如果是一个Promise
if(val instanceof Promise3){
return val
//如果没有参数,或者为null,undefined
}else if(!val){
return new Promise3((resolve, reject) => {resolve()})
//参数存在但不存在then方法
}else if(val && !val.then instanceof Function){
return new Promise3((resolve) => {
resolve(val);
});
//参数存在且存在then方法
}else if(val && val.then instanceof Function){
return new Promise3(val.then)
}
}

实现Promise.reject方法

1
2
3
4
5
6
static reject(val){
//直接将val作为理由返回
return new Promise((resolve,reject)=>{
reject(val);
})
}

实现Promise.all([p1, p2, p3])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static all(arr) {
let res = []
return new Promise((resolve, reject) => {
for(let i = 0, len = arr.length; i < len; i++){
arr[i].then((v) => {
res.push(v)
//是否所有的promise都是resolve
if(res.length === arr.length){
return resolve(res)
}
}, (e) => {
//只要一个reject,直接reject
return reject(e)
})
}
})
}

实现Promise.race([p1, p2, p3])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static race(arr) {
return new Promise((resolve, reject) => {
for (let i = 0, len = arr.length; i < len; i++) {
arr[i].then(
(v) => {
return resolve(v)
},
(e) => {
return reject(e)
}
);
}
});
}

实现Promise.allSettled([p1, p2, p3])

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
static allSettled(arr) {
let res = [];
let count = 0;
return new Promise((resolve, reject) => {
for (let i = 0, len = arr.length; i < len; i++) {
arr[i].then(
(v) => {
res.push({
status: 'fulfilled',
value: v,
});
},
(e) => {
res.push({
status: 'rejected',
reason: e,
});
}
).finally(() => {
//计数器必须在这里统计,因为异步操作,若放在then中,在finally中无法读取预期的值
++count === arr.length && resolve(res)
});
}
});
}

实现Promise.any(p1, p2, p3)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static any(arr) {
let res = [];
return new Promise((resolve, reject) => {
for (let i = 0, len = arr.length; i < len; i++) {
arr[i].then(
(v) => {
//只要有一个resolve,则直接resolve
return resolve(v);
},
(e) => {
res.push(e);
res.length === arr.length && reject(res);
}
);
}
});
}
}

最后

本人能力有限,可能会出现错误,谅解。

Powered by Hexo and Hexo-theme-hiker

Copyright © 2019 - 2024 My Wonderland All Rights Reserved.

UV : | PV :