手写Promise!!

原文 : https://juejin.cn/post/7269640045043777576

首先,Promise肯定是一个类,所以我们才可以new它,然后Promise实例化的时候给它传入一个回调我们叫它executor方法,Promise 内部会立即调用这个executor方法,并且会传入resolvereject两个函数作为调用参数,另外在 Promise 类的原型上应该提供一个then方法,它里面可以传入两个回调,分别为Promise成功的回调Promise失败的回调。调用resolve后会走入成功的回调中,调用reject后会走入失败的回调中

初级版本 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
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

class Promise {
constructor(executor) {
this.value = undefined;
this.reason = undefined;
this.status = PENDING;
// 定义两个数组解决异步调用resolve或者reject的问题
// 充当队列把then里边的回调存起来
// 有的时候,我们会给同一个promise实例执行多次then方法, 那么相应的onFulfilled和onRejected回调必须按照其发起调用的顺序执行。
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];

const resolve = (value) => {
// 当状态为pending状态的时候才可以去改变状态,并且分别将value和reason赋值给对应值,并去执行相应回调函数
if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
this.onResolvedCallbacks.forEach((fn) => fn());
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
this.onRejectedCallbacks.forEach((fn) => fn());
}
};
try {
executor(resolve,reject)
} catch(err) {
reject(err); //有报错会直接执行reject函数将状态变为失败rejected
}
}
}
then(onFulfilled, onRejected) {
//当执行到then的时候,状态已经是fulfilled状态或者是rejected状态,那么就直接执行回调,并且将value/reason作为第一个参数
if (this.status === FULFILLED) {
onFulfilled && onFulfilled(this.value);
}
if (this.status === REJECTED) {
onRejected && onRejected(this.reason);
}
//当执行到then的时候,状态还是pending状态,那么需要将回调存起来,等到状态改变的时候再去执行
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
onFulfilled(this.value);
});
this.onRejectedCallbacks.push(() => {
onRejected(this.reason);
});
}
}
}

module.exports = Promise;

链式调用 Promise

内部调用 then 方法时,返回了一个新的 promise,并让这个新的 promise 接管了它下一个 then 方法。

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
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

class Promise {
constructor(executor) {
this.value = undefined;
this.reason = undefined;
this.status = PENDING;
// 定义两个数组解决异步调用resovle或者reject的问题
// 充当队列把then里边的回调存起来
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
const resolve = (value) => {
if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
this.onResolvedCallbacks.forEach((fn) => fn());
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
this.onRejectedCallbacks.forEach((fn) => fn());
}
};
try {
executor(resolve, reject);
} catch (err) {
reject(err); //有报错会直接执行reject函数将状态变为失败rejected
}
}
then(onFulfilled, onRejected) {
const promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
// onFulfilled方法可能返回值或者promise
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
}
if (this.status === REJECTED) {
// onRejected方法可能返回值或者promise
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
});
this.onRejectedCallbacks.push(() => {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
});
}
});
return promise2;
}
}

module.exports = Promise;

这里最主要的就是resolvePromise,来看下它做了什么:

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
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
// 防止循环引用
return reject(
new TypeError(
"UnhandledPromiseRejectionWarning: TypeError: Chaining cycle detected for promise #<Promise>"
)
);
}
let called; // 声明变量called,相当于加了一把锁,让promise只能调用一次成功或者失败回调,防止死循环。
// 判断x的类型 x是对象或函数才有可能是一个promise
if ((typeof x === "object" && x !== null) || typeof x === "function") {
try {
const then = x.then;
if (typeof then === "function") {
// 只能认为它是一个promise
then.call(
x,
(y) => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject); // 精髓, 递归解析
},
(r) => {
if (called) return;
called = true;
reject(r);
}
);
} else {
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}

因为promiseEventLoop里面是个微任务,不过我们可以简单通过setTimout模拟。

然后我们再加上一些报错的捕获代码以及一些参数的兼容代码,以及实现catch方法。

完全版本 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
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
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";

class Promise {
constructor(executor) {
this.value = undefined;
this.reason = undefined;
this.status = PENDING;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];

const resolve = (value) => {
if (this.status === PENDING) {
this.value = value;
this.status = FULFILLED;
this.onResolvedCallbacks.forEach((fn) => fn());
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason;
this.status = REJECTED;
this.onRejectedCallbacks.forEach((fn) => fn());
}
};

// executor执行时增加try catch,防止执行用户传入的函数直接就报错了,这时我们应该直接reject promise。
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}

then(onFulfilled, onRejected) {
// 这里兼容下 onFulfilled 和 onRejected 的传参
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
onRejected =
typeof onRejected === "function"
? onRejected
: (err) => {
throw err;
};
// 调用onFulfilled和onRejected时,需要包裹setTimeout
const promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
// 用 setTimeout 模拟异步
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === REJECTED) {
// 用 setTimeout 模拟异步
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
// 用 setTimeout 模拟异步
setTimeout(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
// 用 setTimeout 模拟异步
setTimeout(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});

return promise2;
}
// catch函数实际上里面就是调用了then方法
catch(errCallback) {
return this.then(null, errCallback);
}
}

// 解决链式调用
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(
new TypeError(
"UnhandledPromiseRejectionWarning: TypeError: Chaining cycle detected for promise #<Promise>"
)
);
}
let called;
// 判断x的类型 x是对象或函数才有可能是一个promise
if ((typeof x === "object" && x !== null) || typeof x === "function") {
try {
const then = x.then;
if (typeof then === "function") {
// 只能认为它是一个promise
then.call(
x,
(y) => {
if (called) return;
called = true;
resolvePromise(promise2, y, resolve, reject);
},
(r) => {
if (called) return;
called = true;
reject(r);
}
);
} else {
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}

module.exports = Promise;

测试 promise 是否符合规范

  1. 首先安装依赖包 npm i promises-aplus-tests -D

  2. 在我们的代码中添加

    1
    2
    3
    4
    5
    6
    7
    8
    Promise.defer = Promise.deferred = function(){
    let dfd = {};
    dfd.promise = new Promise((resolve, reject)=>{
    dfd.resolve = resolve;
    dfd.reject = reject;
    });
    return dfd;
    }

    注意别忘了 module.exports = Promise;

  3. 运行 promises-aplus-tests 文件名 即可

  4. 有 872 个测试用例,全部通过即可以认为这是一个标准的 promise。

image-20230918151029374

实现Promise的各种方法

Promise 的实例方法有 then/catch/finally 三种,静态方法有 all/race/allSettled/any/resolve/reject 六种

then 和 catch 上面已经实现过了

实现 Promise.resolve

实现 resolve 静态方法有三个要点:

  1. 传参为一个 Promise, 则直接返回它。
  2. 传参为一个 thenable 对象,返回的 Promise 会跟随这个对象,采用它的最终状态作为自己的状态。
  3. 其他情况,直接返回以该值为成功状态的promise对象。
1
2
3
4
5
6
7
8
9
10
11
Promise.resolve = (param) => {
if(param instanceof Promise) return param;
return new Promise((resolve, reject) => {
if(param && param.then && typeof param.then === 'function') {
// param 状态变为成功会调用resolve,将新 Promise 的状态变为成功,反之亦然
param.then(resolve, reject);
}else {
resolve(param);
}
})
}

实现 Promise.reject

Promise.reject 中传入的参数会作为一个 reason 原封不动地往下传, 实现如下:

1
2
3
4
5
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason);
});
}

实现 Promise.prototype.finally

无论当前 Promise 是成功还是失败,调用finally之后都会执行 finally 中传入的函数,并且将值原封不动的往下传。

1
2
3
4
5
6
7
8
9
10
11
Promise.prototype.finally = function(callback) {
this.then(value => {
return Promise.resolve(callback()).then(() => {
return value;
})
}, error => {
return Promise.resolve(callback()).then(() => {
throw error;
})
})
}

实现 Promise.all

对于 all 方法而言,需要完成下面的核心功能:

  1. 传入参数为一个空的可迭代对象,则直接进行resolve
  2. 如果参数中有一个promise失败,那么Promise.all返回的promise对象失败。
  3. 在任何情况下,Promise.all 返回的 promise 的完成状态的结果都是一个数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Promise.all = function(promises) {
return new Promise((resolve, reject) => {
let result = [];
let index = 0;
let len = promises.length;
if(len === 0) {
resolve(result);
return;
}

for(let i = 0; i < len; i++) {
// 为什么不直接 promise[i].then, 因为promise[i]可能不是一个promise
Promise.resolve(promise[i]).then(data => {
result[i] = data;
index++;
if(index === len) resolve(result);
}).catch(err => {
reject(err);
})
}
})
}

实现 Promise.allSettled

Promise.allSettled() 方法只有等到参数数组的所有 Promise 实例都发生状态变更,返回的 Promise 实例才会发生状态变更,无论是执行 resolve 回调还是 reject 回调的状态。
打个比方:多名员工分别同时进行多个项目,你要求每个一个项目都必须完成,然后得到所有项目是令你满意还是令你不满意的。强调的是最终结果。

同时因为 Promise.allSettled() 和 Promise.all() 都是对所有 Promise 实例的一种处理,下面就可以利用 Promise.all() 来实现 Promise.allSettled() 方法。

1
2
3
4
5
6
7
8
9
10
MyPromise.allSettled = function (promises) {
return Promise.all(
promises.map((item) =>
Promise.resolve(item).then(
(value) => ({ status: 'fulfilled', value }),
(reason) => ({ status: 'rejected', reason })
)
)
);
};

实现 Promise.race

race 的实现相比之下就简单一些,只要有一个 promise 执行完,直接 resolve 并停止执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Promise.race = function(promises) {
return new Promise((resolve, reject) => {
if(promises.length === 0) resolve();
else {
let len = promises.length;
if(len === 0) return;
for(let i = 0; i < len; i++) {
Promise.resolve(promise[i]).then(data => {
resolve(data);
return;
}).catch(err => {
reject(err);
return;
})
}
}
})
}

实现 Promise.any

Promise.any() 方法是返回任意一个最快执行 resolve 回调的 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
MyPromise.any = function (promises) {
return new Promise((resolve, reject) => {
if (promises.length === 0) {
return resolve();
} else {
let result = [];
let index = 0;
for (let i = 0; i < promises.length; i++) {
promises[i].then(
(value) => {
return resolve(value);
},
(reason) => {
result[i] = reason;
// 如果所有都失败则返回失败
if (++index === promises.length) {
return reject(new AggregateError(result));
}
}
);
}
}
});
};

over


手写Promise!!
http://example.com/2023/09/18/手写Promise/
作者
weirdo
发布于
2023年9月18日
更新于
2023年10月24日
许可协议