相干链接
- Promises/A+ 标准(原文)
- Promises/A+ 标准(译文)
- 本文代码地址
代码实现
Promise 的状态
enum States { PENDING = "pending", /** * PENDING 状态,此时该 Promise 的后果是悬而未决的,不晓得会被 resolve,或是被 reject。 * 只有当 Promise 的状态为 PENDING 时,resolve 和 reject 函数才能够执行一系列操作,否则只会抛出一个谬误。 **/ FULFILLED = "fulfilled", /** * FULFILLED 状态,阐明此时 Promise 曾经被 resolve,“承诺实现了”。 * 当 Promise 的状态为 FULFILLED 时,then 办法传入的 onFullfilled 函数能力间接退出微工作队列; * 若以后 Promise 的状态为 PENDING,onFullfilled 函数只能退出胜利时的回调函数队列。 **/ REJECTED = "rejected", /** * REJECTED 状态,阐明此时 Promise 曾经被 reject, * 当 Promise 的状态为 REJECTED 时,then 办法传入的 onRejected 函数能力间接退出微工作队列; * 若以后 Promise 的状态为 PENDING,onRejected 函数只能退出失败时的回调函数队列。 **/}
相干类型定义
interface Resolve<T> { (value: T | PromiseLike<T>): void;}interface Reject { (reason?: any): void;}interface Executor<T> { (resolve: Resolve<T>, reject: Reject): void;}/** * Promises/A+ 只是一份标准,任何能通过测试的 Promise 实现都会被这份标准认可,而一些库和框架会实现本人的 Promise,而不是应用原生 ES6 Promise,这就导致了无奈间接应用 p instanceof Promise 来辨认 Promise类型。 * 因而辨认 Promise 是基于鸭子类型(duck typing)检测的,只有是一个 thenable 对象(即定义了 then 办法的对象),即会被辨认为 Promise。 * 同理,下文中 resolvePromise 函数的参数 x 是 PromiseLike 类型而不是 Promise 类型。 **/interface PromiseLike<T> { then<TResult1 = T, TResult2 = never>( onFulfilled?: ((value: T | PromiseLike<T>) => TResult1 | PromiseLike<TResult1>) | undefined | null, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null ): PromiseLike<TResult1 | TResult2>;}
MyPromise 类及其构造函数
Promise 接管一个执行器函数作为参数,该执行器函数的两个参数 (resolve, reject) 由 Promise 外部定义。
class MyPromise<T> { private state: States = States.PENDING; // Promise 的状态 private onFulfilledCbs = [] as (() => void)[]; // 胜利时的回调函数 private onRejectedCbs = [] as (() => void)[]; // 失败时的回调函数 private value!: T | PromiseLike<T>; // 该 Promise 传递的值,是一个非法的 JavaScript 值 private reason: any; // 示意该 Promise 失败起因的值 constructor(executor: Executor<T>) { try { // 作为参数传递给 Promise 的 executor 函数会被立刻调用 executor(this.resolve, this.reject); } catch (e) { this.reject(e); } } resolve: Resolve<T> = (value: T | PromiseLike<T>) => { try { // 因为 js 并没有提供间接用来创立微工作的 api,因而这里用setTimeout模仿创立微工作 setTimeout(() => { if (this.state === States.PENDING) { /** * 仅当 Promise 处于 PENDING 状态时,会执行 resolve 办法的理论逻辑。 * resolve 办法会做以下几件事: * 1. 将 Promise 的状态更新为 FULFILLED; * 2. 将 resolve 承受的 value 值赋值给 Promise 的 value; * 3. 执行 onFulfilledCbs 队列中的所有回调函数,并清空该队列。 **/ this.state = States.FULFILLED; this.value = value; this.onFulfilledCbs.forEach((fn) => fn()); this.onFulfilledCbs = []; } }); } catch (e) { this.reject(e); } }; reject: Reject = (reason: any) => { try { // 用setTimeout模仿创立微工作 setTimeout(() => { if (this.state === States.PENDING) { /** * 仅当 Promise 处于 PENDING 状态时,会执行 reject 办法的理论逻辑。 * reject 办法会做以下几件事: * 1. 将 Promise 的状态更新为 REJECTED; * 2. 将 reject 承受的 reason 值赋值给 Promise 的 reason; * 3. 执行 onRejectedCbs 队列中的所有回调函数,并清空该队列。 **/ this.state = States.REJECTED; this.reason = reason; this.onRejectedCbs.forEach((fn) => fn()); this.onRejectedCbs = []; } }); } catch (e) { this.reject(e); } };}
then 办法
then 办法接管两个可选参数,别离为 Promise 的胜利和失败状况的回调函数。
这两个函数不会立刻被调用,具体情况见代码正文。
then<TResult1 = T, TResult2 = never>( onFulfilled?: ((value: T | PromiseLike<T>) => TResult1 | PromiseLike<TResult1>) | undefined | null, onRejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): MyPromise<TResult1 | TResult2> { // 为确保 onFulfilled 和 onRejected 是函数类型,须要做一个类型转换。 onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (val: T | PromiseLike<T>) => val as any; onRejected = typeof onRejected === "function" ? onRejected : (r: any) => { throw r; }; // then 办法的返回值是一个新的 Promise,这是 Promise 可能链式调用的起因 const promise2 = new MyPromise<TResult1 | TResult2>((resolve: Resolve<TResult1 | TResult2>, reject: Reject) => { if (this.state === States.FULFILLED) { // Promise 已处于 FULFIILED 状态,将 onFulfilled 放入微工作队列中执行。 setTimeout(() => { try { let x = onFulfilled!(this.value); this.resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); } else if (this.state === States.REJECTED) { // Promise 已处于 REJECTED 状态,将 onRejected 放入微工作队列中执行。 setTimeout(() => { try { let x = onRejected!(this.reason); this.resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); } else if (this.state === States.PENDING) { /** * Promise 仍处于 PENDING 状态,尚不能解决回调函数,间接将回调函数退出相应的回调队列。 * 留神,resolve 中调用回调函数是在微工作中进行的,因而这里不再须要创立微工作。 **/ this.onFulfilledCbs.push(() => { try { let x = onFulfilled!(this.value); this.resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); this.onRejectedCbs.push(() => { try { let x = onRejected!(this.reason); this.resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }); } }); return promise2;}
resolvePromise 办法
resolvePromise<T>(promise: MyPromise<T>, x: T | PromiseLike<T>, resolve: Resolve<T>, reject: Reject) { /** * 因为 then 办法的返回值是一个 Promise,如果 then 办法的回调函数同样返回了 Promise,那么就会造成 Promise 的嵌套,因而须要有一个函数对 then 办法回调函数的返回值进行解决,resolvePromise 就是这个函数。 * @param promise then 办法的返回值 * @param x then 回调函数的返回值 **/ // 避免循环援用 if (promise === x) { const e = new TypeError("TypeError: Circular reference"); reject(e); } let called = false; // 避免 resolve 和 reject 屡次调用 if (x && (typeof x === "object" || typeof x === "function")) { try { const then = (x as PromiseLike<T>).then; // 如果 x 是一个 thenable if (typeof then === "function") { then.call( x, (y: T | PromiseLike<T>) => { if (called) return; called = true; // 直到解析的对象不再是 thenable,取出其中的值 this.resolvePromise(promise, y, resolve, reject); }, (r: any) => { if (called) return; called = true; reject(r); } ); } else { resolve(x); } } catch (e) { if (called) return; called = true; reject(e); } } else { resolve(x); }}
测试流程
测试工具文档:Promises/A+ Compliance Test Suite
为了应用官网工具进行测试,须要在程序的最初增加如下代码:
// @ts-ignoreMyPromise.deferred = function () { let deferred: any = {}; deferred.promise = new MyPromise((resolve, reject) => { deferred.resolve = resolve; deferred.reject = reject; }); return deferred;};// @ts-ignoreexport = MyPromise;
将ts代码编译为js
tsc promise.ts
运行测试
npx promises-aplus-tests promise.js
测试后果:
参考文章
- Promise的源码实现(完满合乎Promise/A+标准)
- 实现一个TS版,合乎 Promise/A+标准的 Promise
- 《你不晓得的 JavaScript 中卷》