Promise 是前端面试和工作中极其常见的一个概念,关于它各种方法的手写实现也很有市场,今天在这里总结一下 Promise 基本方法的简单实现。
catch 方法是对 then 方法的封装,只用于接收 reject(reason) 中的错误信息。
因为在 then 方法中 onRejected 参数是可不传的,不传的情况下,错误信息会依次往后传递,直到有 onRejected 函数接收为止,因此在写 promise 链式调用的时候, then 方法不传 onRejected 函数,只需要在最末尾加一个 catch() 就可以了,这样在该链条中的 promise 发生的错误都会被最后的 catch 捕获到。
catch(onRejected) { return this.then(null, onRejected); }
catch 在 promise 链式调用的末尾调用,用于捕获链条中的错误信息,但是 catch 方法内部也可能出现错误,所以有些 promise 实现中增加了一个方法 done 。
done 相当于提供了一个不会出错的 catch 方法,并且不再返回一个 promise ,一般用来结束一个 promise 链。
done() { this.catch(reason => { console.log('done', reason); throw reason; }); }
finally 方法用于无论是 resolve 还是 reject , finall y的参数函数都会被执行。
finally(fn) { return this.then(value => { fn(); return value; }, reason => { fn(); throw reason; }); };
Promise.all 方法接收一个 promise 数组,返回一个新 promise2 ,并发执行数组中的全部 promise ,所有 promise 状态都为 resolved 时, promise2 状态为 resolved 并返回全部 promise 结果,结果顺序和 promise 数组顺序一致。如果有一个 promise 为 rejected 状态,则整个 promise2 进入 rejected 状态。
static all(promiseList) { return new Promise((resolve, reject) => { const result = []; let i = 0; for (const p of promiseList) { p.then(value => { result[i] = value; if (result.length === promiseList.length) { resolve(result); } }, reject); i++; } }); }
Promise.race 方法接收一个 promise 数组, 返回一个新 promise2 ,顺序执行数组中的 promise ,有一个 promise 状态确定, promise2 状态即确定,并且同这个 promise 的状态一致。
static race(promiseList) { return new Promise((resolve, reject) => { for (const p of promiseList) { p.then((value) => { resolve(value); }, reject); } }); }
Promise.resolve 用来生成一个 rejected 完成态的 promise , Promise.reject 用来生成一个 rejected 失败态的 promise 。
static resolve(value) { let promise; promise = new Promise((resolve, reject) => { this.resolvePromise(promise, value, resolve, reject); }); return promise; } static reject(reason) { return new Promise((resolve, reject) => { reject(reason); }); }
Promise 解决过程是一个抽象的操作,其需输入一个 promise 和一个值,我们表示为 [[Resolve]](promise, x),如果 x 有 then 方法且看上去像一个 Promise ,解决程序即尝试使 promise 接受 x 的状态;否则其用 x 的值来执行 promise 。
这种 thenable 的特性使得 Promise 的实现更具有通用性:只要其暴露出一个遵循 Promise/A+ 协议的 then 方法即可;这同时也使遵循 Promise/A+ 规范的实现可以与那些不太规范但可用的实现能良好共存。
运行 [[Resolve]](promise, x) 需遵循以下步骤:
如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
如果 x 为 Promise ,则使 promise 接受 x 的状态:
- 如果 x 处于等待态, promise 需保持为等待态直至 x 被执行或拒绝
- 如果 x 处于执行态,用相同的值执行 promise
- 如果 x 处于拒绝态,用相同的据因拒绝 promise
如果 x 为对象或者函数:
- 把 x.then 赋值给 then
- 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
- 如果 then 是函数,将 x 作为函数的作用域 this 调用之。传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise:
- 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
- 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
- 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
- 如果调用 then 方法抛出了异常 e:
- 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
- 否则以 e 为据因拒绝 promise
- 如果 then 不是函数,以 x 为参数执行 promise
- 如果 x 不为对象或者函数,以 x 为参数执行 promise
如果一个 promise 被一个循环的 thenable 链中的对象解决,而 [[Resolve]](promise, thenable) 的递归性质又使得其被再次调用,根据上述的算法将会陷入无限递归之中。算法虽不强制要求,但也鼓励施者检测这样的递归是否存在,若检测到存在则以一个可识别的 TypeError 为据因来拒绝 promise。
常用的方法基本就这些, Promise 还有很多扩展方法,这里就不一一展示,基本上都是对 then 方法的进一步封装,只要你的 then 方法没有问题,其他方法就都可以依赖 then 方法实现。