编写 Promise 方法

Promise 是什么?

Promise 是异步编程的一种解决方案,它是一个对象,可以获取异步 操作的消息,他的出现大大改善了异步编程的困境,避免了地狱回调, 它比传统的解决方案回调函数和事件更合理和更强大。

所谓 Promise,简单说就是一个容器,里面保存着某个未来才会结束 的事件 (通常是一个异步操作) 的结果。从语法上说,Promise 是一 个对象,从它可以获取异步操作的消息。Promise 提供统一的 API, 各种异步操作都可以用同样的方法进行处理。

  1. Promise 的实例有三个状态:
  • Pending (进行中)

  • Resolved (已完成)

  • Rejected (已拒绝)

当把一件事情交给 promise 时,它的状态就是 Pending,任务完成了 状态就变成了 Resolved、没有完成失败了就变成了 Rejected。

  1. Promise 的实例有两个过程:
    pending -> fulfilled: Resolved (已完成)
    pending -> rejected: Rejected (已拒绝)

    注意:一旦从进行状态变成为其他状态就永远不能更改状态了。

Promise 的特点

对象的状态不受外界影响。promise 对象代表一个异步操作,有三种 状态,pending (进行中)、fulfilled (已成功)、rejected (已失 败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他 操作都无法改变这个状态,这也是 promise 这个名字的由来 ——“承 诺”;

一旦状态改变就不会再变,任何时候都可以得到这个结果。promise 对象的状态改变,只有两种可能:从 pending 变为 fulfilled,从 pending 变为 rejected。这时就称为 resolved (已定型)。如果改 变已经发生了,你再对 promise 对象添加回调函数,也会立即得到这 个结果。这与事件 (event) 完全不同,事件的特点是:如果你错过 了它,再去监听是得不到结果的。

Promise 实现

Promise 实现是通过 js class 编写,主要包括 status、value、error、resolve、reject、then、catch、all、race、allSettled、any 等组成。

  1. 状态
1
2
3
const PROMISE_STATUE_PENDING = "pending"; // 进行中
const PROMISE_STATUE_FULFILLED = "fulfilled"; // 已完成
const PROMISE_STATUE_REJECTED = "rejected"; // 已拒绝
  1. resolve 创建一个已解决的 Promise 对象,将给定的值作为其参数。
1
2
3
4
5
6
7
8
9
10
11
12
resolve = (value) => {
if (this.statue === PROMISE_STATUE_PENDING) {
queueMicrotask(() => {
if (this.statue !== PROMISE_STATUE_PENDING) return;
this.statue = PROMISE_STATUE_FULFILLED;
this.value = value;
this.resFns?.forEach((fn) => {
fn(this.value);
});
});
}
};
  1. reject 创建一个已拒绝的 Promise 对象,将给定的原因作为其参数
1
2
3
4
5
6
7
8
9
10
11
12
reject = (error) => {
if (this.statue === PROMISE_STATUE_PENDING) {
queueMicrotask(() => {
if (this.statue !== PROMISE_STATUE_PENDING) return;
this.statue = PROMISE_STATUE_REJECTED;
this.error = error;
this.errFns.forEach((en) => {
en(this.error);
});
});
}
};
  1. then 添加对 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
constructor(executer) {
this.statue = PROMISE_STATUE_PENDING;
this.value = void 0;
this.error = void 0;
this.resFn;
this.errFn;
const resolve = ((value) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_FULFILLED

queueMicrotask(() => { //queueMicrotask: 主线程执行完毕之后立马执行
this.resfn(value)
})
}
})

const reject = ((error) => {
if (this.status === PROMISE_STATUS_PENDING) {
this.status = PROMISE_STATUS_REJECTED
queueMicrotask(() => {
this.errfn(error)
})
}
})
executer(this.resolve, this.reject);
}

then(resFn, errFn) {
this.resFn = resFn;
this.errFn = errFn;
}
  • 执行结果
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    const p1 = new myPromise((resolve, reject) => {
    resolve(111)
    reject(333333)
    })
    p1.then(res => { //最终打印 1111
    console.log(res);
    }, err => {
    console.log(err);

    })

  • 优化 then 方法

官方给与的 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
this.resFns = [] //1.多次调用then 时用数组 保存
this.errFns = []

// 将then 方法修改为
then(resFn, errFn) {
this.resfns.push(resFn);
this.errFns.push(errFn);
}

// resolve修改为
resolve = (value) => {
if (this.statue === PROMISE_STATUE_PENDING) {
queueMicrotask(() => {
if (this.statue !== PROMISE_STATUE_PENDING) return;
this.statue = PROMISE_STATUE_FULFILLED;
this.value = value;
this.resFns?.forEach((fn) => {
fn(this.value);
});
});
}
};
// reject 修改为
reject = (error) => {
if (this.statue === PROMISE_STATUE_PENDING) {
queueMicrotask(() => {
if (this.statue !== PROMISE_STATUE_PENDING) return;
this.statue = PROMISE_STATUE_REJECTED;
this.error = error;
this.errFns.forEach((en) => {
en(this.error);
});
});
}
};

优化后 then 的运行结果

1
2
3
4
5
6
7
8
9
10
11
12
p1.then(res => {
console.log("res1:", res)
}, err => {
console.log("err1:", err)
})
// 调用then方法多次调用
p1.then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})


运行结果:res2: 111 因为后面的.then 把前面的覆盖掉了 并不会执行res1 所在的代码块
*由此可见 then 方法调用时应该是个数组然后依次调用
下面改造我们的代码then,还需要优化执行resolve 时调用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
then(resFn, errFn) {
const defaultOnRejected = (err) => {
throw err;
};
errFn = errFn || defaultOnRejected;

const defaultOnFulFilled = (value) => {
return value;
};
resFn = resFn || defaultOnFulFilled;

return new MyPromise((resolve, reject) => {
if (this.statue === PROMISE_STATUE_FULFILLED && !!resFn) {
try {
const value = resFn(this.value);
resolve(value);
} catch (error) {
reject(error);
}
}
if (this.statue === PROMISE_STATUE_REJECTED && !!errFn) {
try {
resolve(value);
} catch (error) {
reject(error);
}
}
if (this.statue === PROMISE_STATUE_PENDING) {
if (!!resFn) {
this.resFns.push(() => {
try {
const value = resFn(this.value);
resolve(value);
} catch (error) {
reject(error);
}
});
}

if (!!errFn) {
this.errFns.push(() => {
try {
const value = errFn(this.error);
resolve(value);
} catch (error) {
reject(error);
}
});
}
}
});
}

然后执行:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const p1 = new myPromise((resolve, reject) => {
resolve(111);
reject(333333);
})
p1.then(res => {
console.log("res1:", res);
}, err => {
console.log("err1:", err);
})
// 调用then方法多次调用
p1.then(res => {
console.log("res2:", res);
}, err => {
console.log("err2:", err);
})
执行结果:
res1: 111
res2: 111

  1. catch 添加对 Promise 对象拒绝时的处理程序

    1
    2
    3
    4
    // 添加对Promise对象拒绝时的处理程序。
    catch(errFn) {
    return this.then(undefined, errFn);
    }
  2. finally 添加对 Promise 对象解决或拒绝时的最终处理程序,无论 Promise 对象是否已被解决或拒绝。

    1
    2
    3
    4
    5
    finally(fn) {
    setTimeout(() => {
    fn();
    }, 0);
    }
  3. all 接收一个可迭代对象(如数组),并返回一个新的 Promise 对象。当所有 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
    // 通过类型判断当前数组中的方法或者对象是否为Promise 对象
    const isPromise = function(promise) {
    return (
    !!promise &&
    (typeof promise === "object" || typeof promise === "function") &&
    typeof promise.then === "function"
    );
    };

    /**
    * 接收一个可迭代对象(如数组),并返回一个新的Promise对象。
    * 当所有Promise对象都已解决时,该Promise对象才将被解决,并返回一个包含所有解决值的数组。
    * @param {any[]} iterable
    * @desc 实际上多个对象同步执行时,就相当于把所有的方法重新进行Promise一次。
    * 当遍历到最后一个时,resolve 所有结果。
    *
    */

    MyPromise.all = function(iterable) {
    if (!(iterable instanceof Array)) {
    return console.log("传入参数必须是一个数组");
    }
    return new MyPromise((resolve, reject) => {
    let len = iterable.length;
    let count = 0;
    let results = new Array(len);
    for (let i = 0; i < len; i++) {
    let promise = iterable[i];
    count++;
    if (isPromise(promise)) {
    promise
    .then((res) => {
    results[i] = res;
    if (count === len) {
    resolve(results);
    }
    })
    .catch((err) => {
    reject(err);
    });
    } else if (typeof promise === "function") {
    results[i] = promise();
    } else {
    results[i] = promise;
    }
    }
    // 当数据的所有项都不是promise实例,我们就在这判断多一次,然后resolve
    if (count === len) {
    resolve(results);
    }
    });
    };

    all 运行示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    (async function() {
    const res = MyPromise.all([
    new MyPromise((resolve) => {
    resolve(1);
    }),
    new MyPromise((resolve) => {
    resolve(2);
    }),
    () => {
    return 123;
    },
    88888,
    ]);
    res.then((res) => {
    console.log(res);
    });
    })();
    运行结果: [1, 2, 123, 88888]
  4. race Promise.race (iterable) 传入多个对象,当任何一个执行完成后 resolve 结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
MyPromise.race = function(iterable) {
if (!(iterable instanceof Array)) {
return console.log("传入参数必须是一个数组");
}
return new MyPromise((resolve, reject) => {
iterable.forEach((p) => {
if (isPromise(p)) {
p.then((value) => {
resolve(value);
}).catch((err) => {
reject(err);
});
} else if (typeof p === "function") {
resolve(p());
} else {
resolve(p);
}
});
});
};

race 运行示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(async function() {
const res = MyPromise.race([
new MyPromise((resolve) => {
resolve(1);
}),
new MyPromise((resolve) => {
resolve(2);
}),
]);
res.then((res) => {
console.log(res);
});
})();
运行结果: 1

完整代码

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
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
// status
const PROMISE_STATUE_PENDING = "pending"; // 进行中
const PROMISE_STATUE_FULFILLED = "fulfilled"; // 已完成
const PROMISE_STATUE_REJECTED = "rejected"; // 已拒绝

class MyPromise {
constructor(executer) {
this.statue = PROMISE_STATUE_PENDING;
this.value = void 0;
this.error = void 0;
this.resFns = [];
this.errFns = [];

executer(this.resolve, this.reject);
}
// 创建一个已解决的Promise对象,将给定的值作为其参数。
resolve = (value) => {
if (this.statue === PROMISE_STATUE_PENDING) {
queueMicrotask(() => {
if (this.statue !== PROMISE_STATUE_PENDING) return;
this.statue = PROMISE_STATUE_FULFILLED;
this.value = value;
this.resFns?.forEach((fn) => {
fn(this.value);
});
});
}
};
// 创建一个已拒绝的Promise对象,将给定的原因作为其参数。
reject = (error) => {
if (this.statue === PROMISE_STATUE_PENDING) {
queueMicrotask(() => {
if (this.statue !== PROMISE_STATUE_PENDING) return;
this.statue = PROMISE_STATUE_REJECTED;
this.error = error;
this.errFns.forEach((en) => {
en(this.error);
});
});
}
};
// 添加对Promise对象解决或拒绝时的处理程序。
then(resFn, errFn) {
const defaultOnRejected = (err) => {
throw err;
};
errFn = errFn || defaultOnRejected;

const defaultOnFulFilled = (value) => {
return value;
};
resFn = resFn || defaultOnFulFilled;

return new MyPromise((resolve, reject) => {
if (this.statue === PROMISE_STATUE_FULFILLED && !!resFn) {
try {
const value = resFn(this.value);
resolve(value);
} catch (error) {
reject(error);
}
}
if (this.statue === PROMISE_STATUE_REJECTED && !!errFn) {
try {
resolve(value);
} catch (error) {
reject(error);
}
}
if (this.statue === PROMISE_STATUE_PENDING) {
if (!!resFn) {
this.resFns.push(() => {
try {
const value = resFn(this.value);
resolve(value);
} catch (error) {
reject(error);
}
});
}

if (!!errFn) {
this.errFns.push(() => {
try {
const value = errFn(this.error);
resolve(value);
} catch (error) {
reject(error);
}
});
}
}
});
}
// 添加对Promise对象拒绝时的处理程序。
catch(errFn) {
return this.then(undefined, errFn);
}
// 添加对Promise对象解决或拒绝时的最终处理程序,无论Promise对象是否已被解决或拒绝。
finally(fn) {
setTimeout(() => {
fn();
}, 0);
}
}

const isPromise = function(promise) {
return (
!!promise &&
(typeof promise === "object" || typeof promise === "function") &&
typeof promise.then === "function"
);
};

/**
* 接收一个可迭代对象(如数组),并返回一个新的Promise对象。
* 当所有Promise对象都已解决时,该Promise对象才将被解决,并返回一个包含所有解决值的数组。
* @param {any[]} iterable
*/
MyPromise.all = function(iterable) {
if (!(iterable instanceof Array)) {
return console.log("传入参数必须是一个数组");
}
return new MyPromise((resolve, reject) => {
let len = iterable.length;
let count = 0;
let results = new Array(len);
for (let i = 0; i < len; i++) {
let promise = iterable[i];
count++;
if (isPromise(promise)) {
promise
.then((res) => {
results[i] = res;
if (count === len) {
resolve(results);
}
})
.catch((err) => {
reject(err);
});
} else if (typeof promise === "function") {
results[i] = promise();
} else {
results[i] = promise;
}
}
// 当数据的所有项都不是promise实例,我们就在这判断多一次,然后resolve
if (count === len) {
resolve(results);
}
});
};

MyPromise.race = function(iterable) {
if (!(iterable instanceof Array)) {
return console.log("传入参数必须是一个数组");
}
return new MyPromise((resolve, reject) => {
iterable.forEach((p) => {
if (isPromise(p)) {
p.then((value) => {
resolve(value);
}).catch((err) => {
reject(err);
});
} else if (typeof p === "function") {
resolve(p());
} else {
resolve(p);
}
});
});
};
// const p1 = new MyPromise((resolve, reject) => {
// console.log("状态pending");
// resolve("22222");
// reject("3333333");
// });

// p1.then((res) => {
// console.log("res1:", res);
// return "第二次的成功回调";
// })
// .catch((error) => {
// console.log("err1:", error);
// throw new Error("第二次的失败回调");
// })
// .finally(() => {
// console.log("finally");
// });
// (async function() {
// const res = MyPromise.all([
// new MyPromise((resolve) => {
// resolve(1);
// }),
// new MyPromise((resolve) => {
// resolve(2);
// }),
// () => {
// return 123;
// },
// 88888,
// ]);
// res.then((res) => {
// console.log(res);
// });
// })();

// (async function() {
// const res = MyPromise.race([
// new MyPromise((resolve) => {
// resolve(1);
// }),
// new MyPromise((resolve) => {
// resolve(2);
// }),
// ]);
// res.then((res) => {
// console.log(res);
// });
// })();

Promise 的缺点

无法取消 Promise,一旦新建它就会立即执行,无法中途取消。

如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。

当处于 pending 状态时,无法得知目前进展到哪一个阶段 (刚刚开始 还是即将完成)。

总结:

Promise 对象是异步编程的一种解决方案,最早由社区提出。Promise 是一个构造函数,接收一个函数作为参数,返回一个 Promise 实例。 一个 Promise 实例有三种状态,分别是 pending、resolved 和 rejected,分别代表了进行中、已成功和已失败。实例的状态只能由 pending 转变 resolved 或者 rejected 状态,并且状态一经改变, 就凝固了,无法再被改变了。

状态的改变是通过 resolve () 和 reject () 函数来实现的,可以在 异步操作结束后调用这两个函数改变 Promise 实例的状态,它的原 型上定义了一个 then 方法,使用这个 then 方法可以为两个状态的 改变注册回调函数。这个回调函数属于微任务,会在本轮事件循环的 末尾执行。

注意:在构造 Promise 的时候,构造函数内部的代码是立即执行的。