JS手撕(八) Promise
Promise实现
Promise
的原理之前有写过两篇博客,就不细讲了。
但还是需要简单复习一下下。
Promise构造函数的实现
promise
的状态一开始是pending
,只能从pending
变为resolved
或从pending
变为rejected
。并且是不可逆的。
改变promise
的状态有三种方法,
- 调用 resolve 函数:pending => fulfilled(resolved)
- 调用 reject 函数:pending => rejected
- 抛出异常:pending => rejected
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const p1 = new Promise((resolve, reject) => { });
const p2 = new Promise((resolve, reject) => resolve(123));
const p3 = new Promise((resolve, reject) => reject('error'));
const p4 = new Promise((resolve, reject) => { throw new Error('异常'); });
console.log(p1); console.log(p2); console.log(p3); console.log(p4);
|
从上图可以看出,promise
对象还会有PromiseState
和PromiseResult
属性。
PromiseState
:对象的状态
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 29
| class MyPromise { PromiseState = 'pending';
PromiseResult = undefined;
constructor(executor) {
const resolve = (data) => { if (this.PromiseState === 'pending') { this.PromiseState = 'fulfilled'; this.PromiseResult = data; } };
const reject = (data) => { if (this.PromiseState === 'pending') { this.PromiseState = 'rejected'; this.PromiseResult = data; } };
executor(resolve, reject); } }
|
前3种情况已经能够实现了
1 2 3 4 5 6 7 8 9 10 11 12
| const p1 = new MyPromise((resolve, reject) => { });
const p2 = new MyPromise((resolve, reject) => resolve(123));
const p3 = new MyPromise((resolve, reject) => reject('error'));
console.log(p1); console.log(p2); console.log(p3);
|
但是,抛出异常的情况还不能实现,因为异常没有被捕获,所以会直接报错,后面的代码也不能执行。所以要我们在调用执行器函数executor
时,应该添加try catch
,如果被捕获,那就要执行reject
方法。
1 2 3 4 5
| try { executor(resolve, reject); } catch (err) { reject(err); }
|
then方法的实现
首先,简单的实现一下
1 2 3 4 5 6 7
| then(onResolved, onRejected) { if (this.PromiseState === "fulfilled") { onResolved(this.PromiseResult); } else if (this.PromiseState === "rejected") { onRejected(this.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 29 30
| const p1 = new MyPromise((resolve, reject) => { });
const p2 = new MyPromise((resolve, reject) => resolve(123));
const p3 = new MyPromise((resolve, reject) => reject('error'));
const p4 = new MyPromise((resolve, reject) => { throw new Error('异常'); });
p1.then((data) => { console.log(data); });
p2.then((data) => { console.log(data); });
p3.then(null, (reason) => { console.error(reason); });
p4.then(null, (reason) => { console.error(reason); });
|
完美(×),使用Promise
难免遇到异步任务,所以还需要测试一下。
1 2 3 4 5 6 7 8 9
| const p5 = new MyPromise((resolve, reject) => { setTimeout(() => { resolve('123456'); }, 0) })
p5.then((data) => { console.log(data); })
|
没反应。这是因为因为是异步任务,所以执行then
方法时,状态还是pending
,所以还需要定义数据结构来存回调函数,如果是异步任务,即状态是pending
时,存好回调函数。
1 2 3 4 5 6 7 8 9 10
| then(onResolved, onRejected) { if (this.PromiseState === "fulfilled") { onResolved(this.PromiseResult); } else if (this.PromiseState === "rejected") { onRejected(this.PromiseResult); } else if (this.PromiseState === 'pending') { this.resolvedQueue.push(onResolved); this.rejectedQueue.push(onRejected); } }
|
存好回调函数,自然还是需要调用的。所以resolve
函数和reject
函数最后还需要遍历执行对应回调队列。
1 2 3 4 5 6 7 8 9
| const resolve = (data) => { if (this.PromiseState === 'pending') { this.PromiseState = 'fulfilled'; this.PromiseResult = data;
this.resolvedQueue.forEach(callback => callback(data)); } };
|
reject
函数同理
这样子就能处理异步任务了。
但是,这个时候Promise.then()
方法并不是异步任务。
1 2 3 4 5 6 7 8 9 10
| const p5 = new MyPromise((resolve, reject) => { resolve('123456'); console.log(88888); })
p5.then((res) => { console.log(res) })
console.log(99999);
|
根据JavaScript的执行机制,应该先执行完同步任务,再执行异步任务,并且Promise.then()
是异步任务(微任务),所以应该依次输出88888
、99999
、123456
才对。
所以,then
方法还得改一下,需要添加定时器来让它变成异步任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| then(onResolved, onRejected) { if (this.PromiseState === "fulfilled") { setTimeout(() => { onResolved(this.PromiseResult); })
} else if (this.PromiseState === "rejected") { setTimeout(() => { onRejected(this.PromiseResult); })
} else if (this.PromiseState === 'pending') { this.resolvedQueue.push(onResolved); this.rejectedQueue.push(onRejected); } }
|
小优化:将then
中的参数变为可选。实际上原生Promise.then()
方法的参数不传参数或者只传一个参数都不会影响执行。
原理很简单,只需要在then
方法最前面判断参数是不是函数,不是则把它变成函数即可。
1 2 3 4 5 6 7 8 9
| if (typeof onRejected !== "function") { onRejected = (reason) => { throw reason; }; }
if (typeof onResolved !== "function") { onResolved = (value) => value; }
|
then()
方法返回结果,实现链式调用。原理就是返回一个新的Promise
对象。当然不能只是返回一个Promise
对象就行了,还需要判断回调得到的结果是不是Promise
对象,如果是,还得调用then()
方法,将状态变为fulfilled
或者rejected
,并变更结果为then()
方法返回的值。
如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const p5 = new Promise((resolve, reject) => { resolve('123456'); })
const p6 = p5.then(() => { return 123567; });
const p7 = p6.then(() => { return new Promise((resolve, reject) => { resolve('success'); }) })
console.log(p6); console.log(p7);
|
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
| then(onResolved, onRejected) { if (typeof onRejected !== "function") { onRejected = (reason) => { throw reason; }; }
if (typeof onResolved !== "function") { onResolved = (value) => value; }
return new MyPromise((resolve, reject) => { if (this.PromiseState === "fulfilled") { let result;
setTimeout(() => { result = onResolved(this.PromiseResult);
resolvePromise(result, resolve, reject); })
} else if (this.PromiseState === "rejected") { setTimeout(() => { result = onRejected(this.PromiseResult); resolvePromise(result, resolve, reject); })
} else if (this.PromiseState === 'pending') { this.resolvedQueue.push((() => { resolvePromise(onResolved(this.PromiseResult), resolve, reject) })); this.rejectedQueue.push(() => { resolvePromise(onResolved(this.PromiseResult), resolve, reject) }); } }) }
|
resolvePromise
1 2 3 4 5 6 7 8 9 10
| function resolvePromise(x, resolve, reject) { if (x instanceof MyPromise) {
x.then(resolve, reject); } else { resolve(x); } }
|
测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const p5 = new MyPromise((resolve, reject) => { resolve('123456'); })
const p6 = p5.then(() => { return 123567; });
const p7 = p6.then(() => { return new MyPromise((resolve, reject) => { resolve('success'); }) })
console.log(p6); console.log(p7);
|
catch方法的实现
在搞完上面的then
方法后,catch
方法就迎刃而解了。因为catch
实际上就是一个语法糖,我们也可以用then
方法来实现,只需要不传第一个参数,只传第二个参数即可。
1 2 3
| catch(onRejected) { return this.then(undefined, onRejected); }
|
测试:
1 2 3 4 5 6
| new MyPromise((resolve, reject) => { reject('error'); }) .catch((reason) => { console.error(reason); })
|
完整代码
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
| class MyPromise { constructor(executor) { this.PromiseState = 'pending';
this.PromiseResult = undefined;
this.resolvedQueue = []; this.rejectedQueue = [];
const resolve = (data) => { if (this.PromiseState === 'pending') { this.PromiseState = 'fulfilled'; this.PromiseResult = data;
this.resolvedQueue.forEach(callback => callback(data)); } };
const reject = (data) => { if (this.PromiseState === 'pending') { this.PromiseState = 'rejected'; this.PromiseResult = data;
this.rejectedQueue.forEach(callback => callback(data)); } };
try { executor(resolve, reject); } catch (err) { reject(err); } }
then(onResolved, onRejected) { if (typeof onRejected !== "function") { onRejected = (reason) => { throw reason; }; }
if (typeof onResolved !== "function") { onResolved = (value) => value; }
return new MyPromise((resolve, reject) => { if (this.PromiseState === "fulfilled") { let result;
setTimeout(() => { result = onResolved(this.PromiseResult); resolvePromise(result, resolve, reject); })
} else if (this.PromiseState === "rejected") { setTimeout(() => { result = onRejected(this.PromiseResult); resolvePromise(result, resolve, reject); })
} else if (this.PromiseState === 'pending') { this.resolvedQueue.push((() => { resolvePromise(onResolved(this.PromiseResult), resolve, reject) })); this.rejectedQueue.push(() => { resolvePromise(onResolved(this.PromiseResult), resolve, reject) }); } }) }
catch(onRejected) { return this.then(undefined, onRejected); } }
function resolvePromise(x, resolve, reject) { if (x instanceof MyPromise) {
x.then(resolve, reject); } else { resolve(x); } }
|
参考