原文发布在: 手写 Promise 的相关方法.
摘要
Promise 作为 JS 社区的异步解决方案, 为开发者提供了. then(),Promise.resolve(),Promise.reject() 等基本方法. 除此之外, 为了更方便地组合和控制多个的 Promise 实例, 也提供了. all(),.race() 等方法.
本文会在 Promise 的基本方法上, 手动实现更高级的方法, 来加深对 Promise 的理解:
实现 Promise.all
实现 Promise.race
实现 Promise.any
实现 Promise.allSettled
实现 Promise.finally
完整代码和用例请到 GitHub.com/dongyuanxin/diy-promise.
实现 Promise.all
过程
Promise.all(iterators) 返回一个新的 Promise 实例. iterators 中包含外界传入的多个 promise 实例.
对于返回的新的 Promise 实例, 有以下两种情况:
如果传入的所有 promise 实例的状态均变为 fulfilled, 那么返回的 promise 实例的状态就是 fulfilled, 并且其 value 是 传入的所有 promise 的 value 组成的数组.
如果有一个 promise 实例状态变为了 rejected, 那么返回的 promise 实例的状态立即变为 rejected.
代码实现
实现思路:
传入的参数不一定是数组对象, 可以是 "遍历器"
传入的每个实例不一定是 promise, 需要用 Promise.resolve() 包装
借助 "计数器", 标记是否所有的实例状态均变为 fulfilled
- Promise.myAll = function(iterators) {
- const promises = Array.from(iterators);
- const num = promises.length;
- const resolvedList = new Array(num);
- let resolvedNum = 0;
- return new Promise((resolve, reject) => {
- promises.forEach((promise, index) => {
- Promise.resolve(promise)
- .then(value => {
- // 保存这个 promise 实例的 value
- resolvedList[index] = value;
- // 通过计数器, 标记是否所有实例均 fulfilled
- if (++resolvedNum === num) {
- resolve(resolvedList);
- }
- })
- .catch(reject);
- });
- });
- };
实现 Promise.race
过程
Promise.race(iterators) 的传参和返回值与 Promise.all 相同. 但其返回的 promise 的实例的状态和 value, 完全取决于: 传入的所有 promise 实例中, 最先改变状态那个 (不论是 fulfilled 还是 rejected).
代码实现
实现思路:
某传入实例
pending -> fulfilled
时, 其 value 就是 Promise.race 返回的 promise 实例的 value
某传入实例 pending -> rejected 时, 其 error 就是 Promise.race 返回的 promise 实例的 error
- Promise.myRace = function(iterators) {
- const promises = Array.from(iterators);
- return new Promise((resolve, reject) => {
- promises.forEach((promise, index) => {
- Promise.resolve(promise)
- .then(resolve)
- .catch(reject);
- });
- });
- };
实现 Promise.any
- Promise.any = function(iterators) {
- const promises = Array.from(iterators);
- const num = promises.length;
- const rejectedList = new Array(num);
- let rejectedNum = 0;
- return new Promise((resolve, reject) => {
- promises.forEach((promise, index) => {
- Promise.resolve(promise)
- .then(value => resolve(value))
- .catch(error => {
- rejectedList[index] = error;
- if (++rejectedNum === num) {
- reject(rejectedList);
- }
- });
- });
- });
- };
- const formatSettledResult = (success, value) =>
- success
- ? { status: "fulfilled", value }
- : { status: "rejected", reason: value };
- Promise.allSettled = function(iterators) {
- const promises = Array.from(iterators);
- const num = promises.length;
- const settledList = new Array(num);
- let settledNum = 0;
- return new Promise(resolve => {
- promises.forEach((promise, index) => {
- Promise.resolve(promise)
- .then(value => {
- settledList[index] = formatSettledResult(true, value);
- if (++settledNum === num) {
- resolve(settledList);
- }
- })
- .catch(error => {
- settledList[index] = formatSettledResult(false, error);
- if (++settledNum === num) {
- resolve(settledList);
- }
- });
- });
- });
- };
- new Promise(resolve => {
- setTimeout(() => resolve(1), 1000);
- })
- .then(value => console.log(Date.now()))
- .catch(error => console.log(Date.now()));
- new Promise(resolve => {
- setTimeout(() => resolve(1), 1000);
- }).finally(() => console.log(Date.now()));
- Promise.prototype.finally = function(cb) {
- return this.then(
- value => Promise.resolve(cb()).then(() => value),
- error =>
- Promise.resolve(cb()).then(() => {
- throw error;
- })
- );
- };
- GitHub.com/tc39/proposal-promise-allSettled
- GitHub.com/matthew-andrews/Promise.prototype.finally
来源: http://www.jianshu.com/p/b4e8295c9bf3