手写 Promise 原理
介绍
本章对 Promise 进行一个简易版的描述与实现,具体为:
- 执行器
- 生命周期
- queueMicroTask 微任务队列
- resolve() & reject()
- promise 自身的 taskQueue
- 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
| type Resolve = (value?: any) => void; type Reject = (reject?: any) => void; type OnFulfilled = ((value?: any) => any) | any; type OnRejected = ((reason?: any) => any) | any; type ThenType = ( onFulfilled?: OnFulfilled, onRejected?: OnRejected ) => ZPromise; type Executor = (resolve: Resolve, reject: Reject) => void;
class ZPromise { constructor(executor: Executor) { console.log("exec executor"); executor(); } }
const zPromise = new ZPromise(() => { console.log("sync exec zPromise"); });
|
生命周期
每个 Promise 都会经历一个短暂的生命周期
Promise 的生命周期有两种阶段和三种状态:
两种阶段:
- unsettled:未处理(执行器中未执行到 resolve 或 reject)
- settled:已处理(执行了 resolve 或 reject)
三种状态:
- pending:进行中 (执行器进行中,类似未处理)
- fulfilled:操作成功完成(类似已处理)
- Rejected:由于程序错误或一些原因,又或是主动调用 reject 时触发
1 2 3 4 5 6 7 8 9 10 11 12 13
| class ZPromise { PENDING: string = "pending"; FULFILLED: string = "fulfilled"; REJECTED: string = "rejected"; status: string; constructor(executor: Executor) { this.status = this.PENDING; console.log("init status"); executor(); } }
|
queueMicroTask 微任务队列
当调用 queueMicrotask( 回调 ) 函数后,会将回调加入当前微任务队列末尾
当前同步任务执行完毕后执行微任务队列中的所有微任务
如果有未完成的微任务,则等完毕后才会执行下一个宏任务
resolve() & reject()
当调用这两个函数之后,生命周期就会改变,且是不可逆的
- 调用 resolve 时,微异步执行回调队列中每项的 onFulfilled 回调
- 调用 reject 时,微异步执行回调队列中每项的 onRejected 回调
- 执行器加入错误捕获( 处理程序错误捕获在后面 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 37
| class ZPromise { PENDING: string = "pending"; FULFILLED: string = "fulfilled"; REJECTED: string = "rejected"; status: string; constructor(executor: Executor) { this.status = this.PENDING; try { executor(this.resolve.bind(this), this.reject.bind(this)); } catch (err) { this.reject(err); } }
resolve = (value: any) => { if (this.status === this.PENDING) { this.status = this.FULFILLED; this.value = value; queueMicrotask(() => { this.callbacks.forEach((item) => { item.onFulfilled!(this.value); }); }); } }; reject = (reason: any) => { if (this.status === this.PENDING) { this.status = this.REJECTED; this.value = reason; queueMicrotask(() => { this.callbacks.forEach((item) => { item.onRejected!(this.value); }); }); } }; }
|
promise 自身的 taskQueue
当调用 then( 回调 ) 方法时,会触发一个判断
判断当前生命周期是否为 pending
- 是:将传入的回调添加到当前 Promise 的任务回调队列中(onFulfilled、onRejected)
- 否:将微异步的执行这个回调,并将已处理的值作为回调的入参使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| if (this.status === this.FULFILLED) { queueMicrotask(() => { }); }
if (this.status === this.REJECTED) { queueMicrotask(() => { }); } if (this.status === this.PENDING) { this.callbacks.push({ onFulfilled: (value) => { }, onRejected: (reason) => { }, }); }
|
then()
then() 方法需要返回一个新的 Promise 对象,并支持链式调用,以及错误捕获:
- 判断 then() 入参是否为函数,否则转换成直接返回传入值的函数
- 如果入参回调是函数且有返回值,则将返回值作为新 Promise 对象的已处理方法入参值 ( 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 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
| class ZPromise { PENDING: string = "pending"; FULFILLED: string = "fulfilled"; REJECTED: string = "rejected"; status: string; callbacks: Array<{ onFulfilled?: OnFulfilled; onRejected?: OnRejected }>; value: any = null; constructor(executor: Executor) { this.status = this.PENDING; this.callbacks = []; try { executor(this.resolve.bind(this), this.reject.bind(this)); } catch (err) { this.reject(err); } } resolve = (value: any) => { if (this.status === this.PENDING) { this.status = this.FULFILLED; this.value = value; queueMicrotask(() => { this.callbacks.forEach((item) => { item.onFulfilled!(this.value); }); }); } }; reject = (reason: any) => { if (this.status === this.PENDING) { this.status = this.REJECTED; this.value = reason; queueMicrotask(() => { this.callbacks.forEach((item) => { item.onRejected!(this.value); }); }); } };
then: ThenType = (onFulfilled, onRejected) => { if (typeof onFulfilled !== "function") { onFulfilled = (value) => value; } if (typeof onRejected !== "function") { onRejected = (reason) => reason; }
const promise = new ZPromise((resolve, reject) => { if (this.status === this.FULFILLED) { queueMicrotask(() => { this.parse(onFulfilled?.(this.value), resolve, reject); }); }
if (this.status === this.REJECTED) { queueMicrotask(() => { this.parse(onRejected?.(this.value), resolve, reject); }); } if (this.status === this.PENDING) { this.callbacks.push({ onFulfilled: (value) => { this.parse(onFulfilled?.(value), resolve, reject); }, onRejected: (reason) => { this.parse(onRejected?.(reason), resolve, reject); }, }); } }); return promise; };
parse = (result: any, resolve: Resolve, reject: Reject) => { try { resolve(result); } catch (err) { reject(err); } }; }
|
测试一下
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
| const p = new ZPromise((resolve, reject) => { setTimeout(() => { resolve(); }); }); const p2 = new ZPromise((resolve, reject) => { setTimeout(() => { resolve(); }); }); const p3 = new ZPromise((resolve, reject) => { resolve("5"); }); const p4 = new ZPromise((resolve, reject) => { reject("6"); });
p.then(() => { console.log("1"); }).then(() => { console.log("2"); }); p2.then(() => { console.log("3"); }); p.then(() => { console.log("4"); }); p3.then((res) => { console.log(res); return "7"; }).then((res) => { console.log(res); }); p4.then(null, (res) => { console.log(res); }); p3.then((res) => { console.log("8"); });
|