看了就会,手写 Promise 全部 API 教程,包括处于 TC39 第四阶段草案的 Promise.any()


在这里插入图片描述

前言

我们在上篇用了很大功夫实现了 Promise 的核心方法,并且通过了 Promises/A+ 官方872个测试用例测试,接下来实现这些静态方法已经是小菜一碟了,因为这些 API 全部是对前面的封装而已。

上篇文章在这里:手把手一行一行代码教你“手写Promise“,完美通过 Promises/A+ 官方872个测试用例

官方 Promise 还有很多API ,除了 then 方法以外还有 两个实例方法:

  • Promise.prototype.catch
  • Promise.prototype.finally

◾ 以及目前 Promise 规范的 六个静态方法

  • Promise.resolve
  • Promise.reject
  • Promise.all
  • Promise.allSettled
  • Promise.any
  • Promise.race

在这里插入图片描述

虽然这些都不在 Promise/A+ 规范里面,但是我们也来实现一下吧,加深理解。其实我们前面我们用了很大功夫实现了 Promise/A+ ,现在再来实现这些已经是小菜一碟了,因为这些API全部是前面的封装而已。

1. 实现 Promise.resolve

Promise.resolve(value) 将给定的一个值转为Promise对象。

  • 如果这个值是一个 promise ,那么将返回这个 promise ;
  • 如果这个值是thenable(即带有"then"方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态;
  • 否则返回的promise将以此值完成,即以此值执行resolve()方法 (状态为fulfilled)。

根据规范我们这样实现(写法一):

class myPromise {
    ...
}

function resolvePromise(promise2, x, resolve, reject) {
    ...
}

/**
 * Promise.resolve()
 * @param {[type]} value 要解析为 Promise 对象的值 
 */
+ myPromise.resolve = function (value) {
+     // 如果这个值是一个 promise ,那么将返回这个 promise 
+     if (value instanceof myPromise) {
+         return value;
+     } else if (value instanceof Object && 'then' in value) {
+         // 如果这个值是thenable(即带有`"then" `方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态;
+         return new myPromise((resolve, reject) => {
+             value.then(resolve, reject);
+         })
+     }
+     
+     // 否则返回的promise将以此值完成,即以此值执行`resolve()`方法 (状态为fulfilled)
+     return new myPromise((resolve) => {
+         resolve(value)
+     })
+ }

module.exports = myPromise;

使用官方例子测试一下:

const myPromise = require('./promiseOtherAPI');

const promise1 = myPromise.resolve(123);

promise1.then((value) => {
  console.log(value);
  // expected output: 123
});

// Resolve一个thenable对象
var p1 = myPromise.resolve({
    then: function (onFulfill) {
        onFulfill("Resolving");
    }
});
console.log(p1 instanceof myPromise) // true, 这是一个Promise对象

setTimeout(() => {
    console.log('p1 :>> ', p1);
}, 1000);

p1.then(function (v) {
    console.log(v); // 输出"fulfilled!"
}, function (e) {
    // 不会被调用
});

// Thenable在callback之前抛出异常
// myPromise rejects
var thenable = {
    then: function (resolve) {
        throw new TypeError("Throwing");
        resolve("Resolving");
    }
};

var p2 = myPromise.resolve(thenable);
p2.then(function (v) {
    // 不会被调用
}, function (e) {
    console.log(e); // TypeError: Throwing
});

输出结果:

true
123
Resolving
TypeError: Throwing
p1 :>> myPromise {PromiseState: 'fulfilled', PromiseResult: 'Resolving', onFulfilledCallbacks: Array(1), onRejectedCallbacks: Array(1)}

在这里插入图片描述

测试通过 ✌

静态方法改造

类(class)通过 static 关键字定义静态方法。不能在类的实例上调用静态方法,而应该通过类本身调用。这些通常是实用程序方法,例如创建或克隆对象的功能。

类相当于实例的原型,所有在类中定义的方法,都会被实例继承。如果在一个方法前,加上static关键字,就表示该方法不会被实例继承,而是直接通过类来调用,这就称为“静态方法”。

写法二、使用静态方法 static:

class myPromise {
	...
	
    resolve(result) {
		...
    }

    reject(reason) {
		...
    }

    then(onFulfilled, onRejected) {
		...
    }

    /**
     * Promise.resolve()
     * @param {[type]} value 要解析为 Promise 对象的值 
     */
+   static resolve(value) {
+       // 如果这个值是一个 promise ,那么将返回这个 promise 
+       if (value instanceof myPromise) {
+           return value;
+       } else if (value instanceof Object && 'then' in value) {
+           // 如果这个值是thenable(即带有`"then" `方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态;
+           return new myPromise((resolve, reject) => {
+               value.then(resolve, reject);
+           })
+       }
+
+       // 否则返回的promise将以此值完成,即以此值执行`resolve()`方法 (状态为fulfilled)
+       return new myPromise((resolve) => {
+           resolve(value)
+       })
+   }
}

function resolvePromise(promise2, x, resolve, reject) {
	...
}

module.exports = myPromise;

2. 实现 Promise.reject

Promise.reject()方法返回一个带有拒绝原因的Promise对象。

官方例子:

Promise.reject(new Error('fail')).then(function() {
  // not called
}, function(error) {
  console.error(error); // Stacktrace
});

输出结果:
在这里插入图片描述
根据规范我们这样实现(写法一):

class myPromise {
	...
}

function resolvePromise(promise2, x, resolve, reject) {
	...
}

myPromise.resolve = function (value) {
	...
}

/**
 * myPromise.reject
 * @param {*} reason 表示Promise被拒绝的原因
 * @returns 
 */
+ myPromise.reject = function (reason) {
+     return new myPromise((resolve, reject) => {
+         reject(reason);
+     })
+ }

module.exports = myPromise;

使用官方用例测试一下:

const myPromise = require('./promiseOtherAPI')

myPromise.reject(new Error('fail')).then(function () {
    // not called
}, function (error) {
    console.error(error); // Error: fail
});

输出结果:

Error: fail

在这里插入图片描述
测试通过 ✌

写法二、使用静态方法 static:

class myPromise {
	...
	
    resolve(result) {
		...
    }

    reject(reason) {
		...
    }

    then(onFulfilled, onRejected) {
		...
    }

    /**
     * Promise.resolve()
     * @param {[type]} value 要解析为 Promise 对象的值 
     */
    static resolve(value) {
        // 如果这个值是一个 promise ,那么将返回这个 promise 
        if (value instanceof myPromise) {
            return value;
        } else if (value instanceof Object && 'then' in value) {
            // 如果这个值是thenable(即带有`"then" `方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态;
            return new myPromise((resolve, reject) => {
                value.then(resolve, reject);
            })
        }

        // 否则返回的promise将以此值完成,即以此值执行`resolve()`方法 (状态为fulfilled)
        return new myPromise((resolve) => {
            resolve(value)
        })
    }

    /**
     * myPromise.reject
     * @param {*} reason 表示Promise被拒绝的原因
     * @returns 
     */
+   static reject(reason) {
+       return new myPromise((resolve, reject) => {
+           reject(reason);
+       })
+   }
}

function resolvePromise(promise2, x, resolve, reject) {
	...
}

module.exports = myPromise;

3. 实现 Promise.prototype.catch

catch() 方法返回一个Promise,并且处理拒绝的情况。它的行为与调用Promise.prototype.then(undefined, onRejected) 相同。

事实上, calling obj.catch(onRejected) 内部calls obj.then(undefined, onRejected)。(这句话的意思是,我们显式使用obj.catch(onRejected),内部实际调用的是obj.then(undefined, onRejected))

Promise.prototype.catch()方法是.then(null, rejection).then(undefined, rejection)的别名,用于指定发生错误时的回调函数。

因此我们可以这样来实现:

class myPromise {
	...
	
    then(onFulfilled, onRejected) {
		...
    }

+   catch (onRejected) {
+       return this.then(undefined, onRejected)
+   }
}

function resolvePromise(promise2, x, resolve, reject) {
	...
}

module.exports = myPromise;

就一行代码,我的天,居然这么简单😱

我们用官方例子来测试一下吧

const myPromise = require('./promiseOtherAPI')

var p1 = new myPromise(function (resolve, reject) {
    resolve('Success');
});

p1.then(function (value) {
    console.log(value); // "Success!"
    throw 'oh, no!';
}).catch(function (e) {
    console.log(e); // "oh, no!"
}).then(function () {
    console.log('after a catch the chain is restored');
}, function () {
    console.log('Not fired due to the catch');
});

// 以下行为与上述相同
p1.then(function (value) {
    console.log(value); // "Success!"
    return Promise.reject('oh, no!');
}).catch(function (e) {
    console.log(e); // "oh, no!"
}).then(function () {
    console.log('after a catch the chain is restored');
}, function () {
    console.log('Not fired due to the catch');
});

// 捕获异常
const p2 = new myPromise(function (resolve, reject) {
    throw new Error('test');
});
p2.catch(function (error) {
    console.log(error);
});
// Error: test

输出:

Success
Success
Error: test
oh, no!
oh, no!
after a catch the chain is restored
after a catch the chain is restored

测试通过,没毛病😏

4. 实现 Promise.prototype.finally

finally() 方法返回一个Promise。在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。这为在Promise是否成功完成后都需要执行的代码提供了一种方式。

这避免了同样的语句需要在then()catch()中各写一次的情况。该方法是 ES2018 引入标准的。

由于无法知道promise的最终状态,所以finally的回调函数中不接收任何参数,它仅用于无论最终结果如何都要执行的情况。

根据规范我们这样实现:

class myPromise {
	...
    catch (onRejected) {
        return this.then(undefined, onRejected)
    }

    /**
     * finally
     * @param {*} callBack 无论结果是fulfilled或者是rejected,都会执行的回调函数
     * @returns 
     */
+   finally(callBack) {
+       return this.then(callBack, callBack)
+   }
}

function resolvePromise(promise2, x, resolve, reject) {
	...
}

myPromise.resolve = function (value) {
	...
}

myPromise.reject = function (reason) {
	...
}

module.exports = myPromise;

对,就这么简单 ✌

测试一下:

const myPromise = require('./promiseOtherAPI')

let p1 = new Promise(function (resolve, reject) {
    resolve(1)
}).then(function (value) {
    console.log(value);
}).catch(function (e) {
    console.log(e);
}).finally(function () {
    console.log('finally');
});

输出结果:

1
finally

测试通过 👏👏👏

5. 实现 Promise.all

Promise.all() 方法接收一个promiseiterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入,并且只返回一个Promise实例, 输入的所有promiseresolve回调的结果是一个数组。

返回的这个Promise的resolve回调执行是在所有输入的promise的resolve回调都结束,或者输入的iterable里没有promise了的时候。它的reject回调执行是,只要任何一个输入的promise的reject回调执行或者输入不合法的promise就会立即抛出错误,并且reject的是第一个抛出的错误信息。

  • Promise.all 等待所有都完成(或第一个失败)
  • 如果传入的参数是一个空的可迭代对象,则返回一个已完成(already resolved)状态的 Promise
  • 如果参数中包含非 promise 值,这些值将被忽略,但仍然会被放在返回数组中,如果 promise 完成的话 (也就是如果参数里的某值不是Promise,则需要原样返回在数组里)
  • 在任何情况下,Promise.all 返回的 promise 的完成状态的结果都是一个数组,它包含所有的传入迭代参数对象的值(也包括非 promise 值)。
  • 如果传入的 promise 中有一个失败(rejected),Promise.all 异步地将失败的那个结果给失败状态的回调函数,而不管其它 promise 是否完成

根据规范我们这样实现:

class myPromise {
	...

    resolve(result) {
		...
    }
    reject(reason) {
		...
    }
    then(onFulfilled, onRejected) {
		...
    }
    static resolve(value) {
		...
    }
    static reject(reason) {
		...
    }
    catch (onRejected) {
		...
    }
    finally(callBack) {
		...
    }

    /**
     * Promise.all
     * @param {iterable} promises 一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入
     * @returns 
     */
+   static all(promises) {
+       return new myPromise((resolve, reject) => {
+           // 参数校验
+           if (Array.isArray(promises)) {
+               let result = []; // 存储结果
+               let count = 0; // 计数器
+
+               // 如果传入的参数是一个空的可迭代对象,则返回一个已完成(already resolved)状态的 Promise
+               if (promises.length === 0) {
+                   return resolve(promises);
+               }
+
+               promises.forEach((item, index) => {
+                   //  判断参数是否为promise
+                   if (item instanceof myPromise) {
+                       myPromise.resolve(item).then(
+                           value => {
+                               count++;
+                               // 每个promise执行的结果存储在result中
+                               result[index] = value;
+                               // Promise.all 等待所有都完成(或第一个失败)
+                               count === promises.length && resolve(result);
+                           },
+                           reason => {
+                               /**
+                                * 如果传入的 promise 中有一个失败(rejected),
+                                * Promise.all 异步地将失败的那个结果给失败状态的回调函数,而不管其它 promise 是否完成
+                                */
+                               reject(reason);
+                           }
+                       )
+                   } else {
+                       // 参数里中非Promise值,原样返回在数组里
+                       count++;
+                       result[index] = item;
+                       count === promises.length && resolve(result);
+                   }
+               })
+           } else {
+               return reject(new TypeError('Argument is not iterable'))
+           }
+       })
+   }
}

function resolvePromise(promise2, x, resolve, reject) {
	...
}

module.exports = myPromise;

使用官方例子测试一下:

const myPromise = require('../promiseOtherAPI');

const promise1 = myPromise.resolve(3);
const promise2 = 42;
const promise3 = new myPromise((resolve, reject) => {
    setTimeout(resolve, 100, 'foo');
});

myPromise.all([promise1, promise2, promise3]).then((values) => {
    console.log(values);
});
// expected output: Array [3, 42, "foo"]

输出结果:

(3) [3, 42, 'foo']

在这里插入图片描述
测试通过 👏👏👏

测试 Promise.all 的快速返回失败行为

Promise.all 在任意一个传入的 promise 失败时返回失败。例如,如果你传入的 promise中,有四个 promise 在一定的时间之后调用成功函数,有一个立即调用失败函数,那么 Promise.all 将立即变为失败。

const myPromise = require('../promiseOtherAPI');

var p1 = new Promise((resolve, reject) => {
    setTimeout(resolve, 1000, 'one');
});
var p2 = new Promise((resolve, reject) => {
    setTimeout(resolve, 2000, 'two');
});
var p3 = new Promise((resolve, reject) => {
    setTimeout(resolve, 3000, 'three');
});
var p4 = new Promise((resolve, reject) => {
    setTimeout(resolve, 4000, 'four');
});
var p5 = new Promise((resolve, reject) => {
    reject('reject');
});

Promise.all([p1, p2, p3, p4, p5]).then(values => {
    console.log(values);
}, reason => {
    console.log(reason)
});

//From console:
//"reject"

输出结果:
在这里插入图片描述
测试通过 👏👏👏

6. 实现 Promise.allSettled

Promise.allSettled(iterable)方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果。

  • 当你有多个彼此不依赖的异步任务成功完成时,或者你总是想知道每个promise的结果时,通常使用它。

  • 相比之下,Promise.all() 更适合彼此相互依赖或者在其中任何一个reject时立即结束。

参数 iterable 是一个可迭代的对象,例如Array,其中每个成员都是Promise。

对于每个结果对象,都有一个 status 字符串。如果它的值为 fulfilled,则结果对象上存在一个 value 。如果值为 rejected,则存在一个 reason 。value(或 reason )反映了每个 promise 决议(或拒绝)的值。

在实现前我们需要验证一点,输入的非promise值应该怎么处理?

为此我们在 Promise.allSettled(iterable) 的参数 iterable 中传入一个非promise值,看一下 Promise.allSettled() 方法内部会怎么处理:

const promise1 = Promise.resolve(3);
const promise2 = 1;
const promises = [promise1, promise2];

Promise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result)));

输出结果:

{status: 'fulfilled', value: 3}
{status: 'fulfilled', value: 1}

在这里插入图片描述
我们发现 Promise.allSettled() 方法内部将非 Promise 值转换成 Promise 了

那下面我们就将非 Promise 值通过 Promise.resolve 转换为 Promise 进行统一处理

根据规范我们这样实现:

class myPromise {
    ...

    resolve(result) {
        ...
    }

    reject(reason) {
        ...
    }

    then(onFulfilled, onRejected) {
        ...
    }

    static resolve(value) {
        ...
    }

    static reject(reason) {
        ...
    }

    catch (onRejected) {
        ...
    }

    finally(callBack) {
        ...
    }

    static all(promises) {
        ...
    }

    /**
     * Promise.allSettled 
     * @param {*} promises 一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入
     * @returns 
     */
+   static allSettled(promises) {
+       return new myPromise((resolve, reject) => {
+           // 参数校验
+           if (Array.isArray(promises)) {
+               let result = []; // 存储结果
+               let count = 0; // 计数器
+
+               // 如果传入的是一个空数组,那么就直接返回一个resolved的空数组promise对象
+               if (promises.length === 0) return resolve(promises);
+
+               promises.forEach((item, index) => {
+                   // 非promise值,通过Promise.resolve转换为promise进行统一处理
+                   myPromise.resolve(item).then(
+                       value => {
+                           count++;
+                           // 对于每个结果对象,都有一个 status 字符串。如果它的值为 fulfilled,则结果对象上存在一个 value 。
+                           result[index] = {
+                               status: 'fulfilled',
+                               value
+                           }
+                           // 所有给定的promise都已经fulfilled或rejected后,返回这个promise
+                           count === promises.length && resolve(result);
+                       },
+                       reason => {
+                           count++;
+                           /**
+                            * 对于每个结果对象,都有一个 status 字符串。如果值为 rejected,则存在一个 reason 。
+                            * value(或 reason )反映了每个 promise 决议(或拒绝)的值。
+                            */
+                           result[index] = {
+                               status: 'rejected',
+                               reason
+                           }
+                           // 所有给定的promise都已经fulfilled或rejected后,返回这个promise
+                           count === promises.length && resolve(result);
+                       }
+                   )
+               })
+           } else {
+               return reject(new TypeError('Argument is not iterable'))
+           }
+       })
+   }
}

function resolvePromise(promise2, x, resolve, reject) {
    ...
}

module.exports = myPromise;

使用官方例子测试一下:

const myPromise = require('../promiseOtherAPI');

const promise1 = myPromise.resolve(3);
const promise2 = 1;
const promises = [promise1, promise2];

myPromise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result)));

setTimeout(() => {
    const p1 = myPromise.resolve(3);
    const p2 = new myPromise((resolve, reject) => setTimeout(reject, 100, 'foo'));
    const ps = [p1, p2];

    myPromise.allSettled(ps).
    then((results) => results.forEach((result) => console.log(result)));
}, 1000);

myPromise.allSettled([]).then((results) => console.log(results))

输出结果:

(0) []
{status: 'fulfilled', value: 3}
{status: 'fulfilled', value: 1}
{status: 'fulfilled', value: 3}
{status: 'rejected', reason: 'foo'}

在这里插入图片描述
测试通过 perfect ✌✌✌

7. 实现 Promise.any

本质上,这个方法和Promise.all()是相反的。

Promise.any() 接收一个Promise可迭代对象,只要其中的一个 promise 成功,就返回那个已经成功的 promise 。

如果可迭代对象中没有一个 promise 成功(即所有的 promises 都失败/拒绝),就返回一个失败的 promise 和AggregateError类型的实例,它是 Error 的一个子类,用于把单一的错误集合在一起。

  • 如果传入的参数是一个空的可迭代对象,则返回一个 已失败(already rejected) 状态的 Promise。
  • 如果传入的参数不包含任何 promise,则返回一个 异步完成 (asynchronously resolved)的 Promise。(即将非Promise值,转换为Promise并当做成功)
  • 只要传入的迭代对象中的任何一个 promise 变成成功(resolve)状态,或者其中的所有的 promises 都失败,那么返回的 promise 就会 异步地(当调用栈为空时) 变成成功/失败(resolved/reject)状态。(如果所有Promise都失败,则报错)

注意! Promise.any() 方法依然是实验性的,尚未被所有的浏览器完全支持。它当前处于 TC39 第四阶段草案(Stage 4)

在这里插入图片描述

node v14.15.4 版本下测试 Promise.any() 发现还没有被支持:

Uncaught TypeError: Promise.any is not a function
在这里插入图片描述
在这里插入图片描述

既然是处于草案阶段的实验性 API ,如果想要在各版本浏览器中兼容性使用,那实现 Promise.any() 就很有必要 💪

根据规范我们这样实现:

class myPromise {
    ...

    resolve(result) {
        ...
    }

    reject(reason) {
        ...
    }

    then(onFulfilled, onRejected) {
        ...
    }

    static resolve(value) {
        ...
    }

    static reject(reason) {
        ...
    }

    catch (onRejected) {
        ...
    }

    finally(callBack) {
        ...
    }

    static all(promises) {
        ...
    }

    static allSettled(promises) {
        ...
    }


    /**
     * Promise.any()
     * @param {iterable} promises 一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入
     * @returns 
     */
+   static any(promises) {
+       return new myPromise((resolve, reject) => {
+           // 参数校验
+           if (Array.isArray(promises)) {
+               let errors = []; // 
+               let count = 0; // 计数器
+
+               // 如果传入的参数是一个空的可迭代对象,则返回一个 已失败(already rejected) 状态的 Promise。
+               if (promises.length === 0) return reject(new AggregateError('All promises were rejected'));
+
+               promises.forEach(item => {
+                   // 非Promise值,通过Promise.resolve转换为Promise
+                   myPromise.resolve(item).then(
+                       value => {
+                           // 只要其中的一个 promise 成功,就返回那个已经成功的 promise 
+                           resolve(value);
+                       },
+                       reason => {
+                           cout++;
+                           errors.push(reason);
+                           /**
+                            * 如果可迭代对象中没有一个 promise 成功,就返回一个失败的 promise 和AggregateError类型的实例,
+                            * AggregateError是 Error 的一个子类,用于把单一的错误集合在一起。
+                            */
+                           cout === promises.length && reject(new AggregateError(errors));
+                       }
+                   )
+               })
+           } else {
+               return reject(new TypeError('Argument is not iterable'))
+           }
+       })
+   }
}

function resolvePromise(promise2, x, resolve, reject) {
    ...
}

module.exports = myPromise;

使用官方例子测试一下:
在这里插入图片描述
发现报错了,提示 AggregateErro 没有定义,这里不是我们代码的问题,是因为这个 AggregateErro ,node v14.15.4 版本没有支持,按理说这个版本已经很高了,为什么还没有支持呢?

和 Promise.any() 一样,这个 AggregateError 也是一个实验中的功能,处于Stage 3 Draft (第三阶段草案):

在这里插入图片描述
我们通过升级 node 版本来兼容这些处于草案阶段的实验功能~

node v14.15.4 升级到最新的 node v16.13.0
在这里插入图片描述
再次验证即可通过:
在这里插入图片描述
用其他用例测试一下该方法:

const myPromise = require('../myPromiseFully');

/**
 * 验证Promise.any()方法
 */

// console.log(new AggregateError('All promises were rejected'));

myPromise.any([]).catch(e => {
    console.log(e);
});

const pErr = new Promise((resolve, reject) => {
    reject("总是失败");
});

const pSlow = new Promise((resolve, reject) => {
    setTimeout(resolve, 500, "最终完成");
});

const pFast = new Promise((resolve, reject) => {
    setTimeout(resolve, 100, "很快完成");
});

Promise.any([pErr, pSlow, pFast]).then((value) => {
    console.log(value);
    // 期望输出: "很快完成"
})


const pErr1 = new myPromise((resolve, reject) => {
    reject("总是失败");
});

const pErr2 = new myPromise((resolve, reject) => {
    reject("总是失败");
});

const pErr3 = new myPromise((resolve, reject) => {
    reject("总是失败");
});

myPromise.any([pErr1, pErr2, pErr3]).catch(e => {
    console.log(e);
})

输出结果:

AggregateError: All promises were rejected
AggregateError: All promises were rejected
很快完成

测试通过 😊

8. 实现 Promise.race()

Promise.race(iterable) 方法返回一个 promise,一旦迭代器中的某个promise解决或拒绝,返回的 promise就会解决或拒绝。

一个待定的 Promise 只要给定的迭代中的一个promise解决或拒绝,就采用第一个promise的值作为它的返回值,从而异步地解析或拒绝(一旦堆栈为空)。

race 函数返回一个 Promise,它将与第一个传递的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失败(rejects),这要取决于第一个完成的方式是两个中的哪个。

  • 如果传的迭代是空的,则返回的 promise 将永远等待。

  • 如果迭代包含一个或多个非承诺值和/或已解决/拒绝的承诺,则 Promise.race 将解析为迭代中找到的第一个值。

根据规范我们这样实现:

class myPromise {
	...

    static resolve(value) {
		...
    }

    static reject(reason) {
		...
    }

    catch (onRejected) {
		...
    }

    finally(callBack) {
		...
    }

    static all(promises) {
		...
    }

    static allSettled(promises) {
		...
    }

    static any(promises) {
    	...
    }

    /**
     * Promise.race()
     * @param {iterable} promises 可迭代对象,类似Array。详见 iterable。
     * @returns 
     */
+   static race(promises) {
+       return new myPromise((resolve, reject) => {
+           // 参数校验
+           if (Array.isArray(promises)) {
+               // 如果传入的迭代promises是空的,则返回的 promise 将永远等待。
+               if (promises.length > 0) {
+                   promises.forEach(item => {
+                       /**
+                        * 如果迭代包含一个或多个非承诺值和/或已解决/拒绝的承诺,
+                        * 则 Promise.race 将解析为迭代中找到的第一个值。
+                        */
+                       myPromise.resolve(item).then(resolve, reject);
+                   })
+               }
+           } else {
+               return reject(new TypeError('Argument is not iterable'))
+           }
+       })
+   }
}

function resolvePromise(promise2, x, resolve, reject) {
	...
}

module.exports = myPromise;

最后测试一下代码:

const myPromise = require('../myPromiseFully');

/**
 * 验证Promise.race()方法
 */

// 数组全是非Promise值,测试通过
let p1 = myPromise.race([1, 3, 4]);
setTimeout(() => {
    console.log('p1 :>> ', p1);
});

// 空数组,测试通过
let p2 = myPromise.race([]);
setTimeout(() => {
    console.log('p2 :>> ', p2);
});

const p11 = new myPromise((resolve, reject) => {
    setTimeout(resolve, 500, 'one');
});

const p22 = new myPromise((resolve, reject) => {
    setTimeout(resolve, 100, 'two');
});

// // 数组里有非Promise值,测试通过
myPromise.race([p11, p22, 10]).then((value) => {
    console.log('p3 :>> ', value);
    // Both resolve, but p22 is faster
});
// expected output: 10

// 数组里有'已解决的Promise' 和 非Promise值 测试通过
let p12 = myPromise.resolve('已解决的Promise')
setTimeout(() => {
    myPromise.race([p12, p22, 10]).then((value) => {
        console.log('p4 :>> ', value);
    });
    // expected output:已解决的Promise
});

// Promise.race的一般情况 测试通过
const p13 = new myPromise((resolve, reject) => {
    setTimeout(resolve, 500, 'one');
});

const p14 = new myPromise((resolve, reject) => {
    setTimeout(resolve, 100, 'two');
});

myPromise.race([p13, p14]).then((value) => {
    console.log('p5 :>> ', value);
    // Both resolve, but promise2 is faster
});
// expected output: "two"

输出结果为:

p1 :>>  myPromise {PromiseState: 'pending', PromiseResult: null, onFulfilledCallbacks: Array(0), onRejectedCallbacks: Array(0)}
p2 :>>  myPromise {PromiseState: 'pending', PromiseResult: null, onFulfilledCallbacks: Array(0), onRejectedCallbacks: Array(0)}
p3 :>>  10
p4 :>>  已解决的Promise
p5 :>>  two

测试通过 🎉🎉🎉

完整代码

手写 Promise 全部方法的完整版代码较长,这里如果看不清楚的可以去我的GitHub上看:

或者

/**
 * 在 myPromise.js 基础上,根据规范实现了 Promise 的全部方法:
 * - Promise.resolve()
 * - Promise.reject()
 * - Promise.prototype.catch()
 * - Promise.prototype.finally()
 * - Promise.all()
 * - Promise.allSettled()
 * - Promise.any()
 * - Promise.race()
 */
class myPromise {
    static PENDING = 'pending';
    static FULFILLED = 'fulfilled';
    static REJECTED = 'rejected';

    constructor(func) {
        this.PromiseState = myPromise.PENDING;
        this.PromiseResult = null;
        this.onFulfilledCallbacks = [];
        this.onRejectedCallbacks = [];
        try {
            func(this.resolve.bind(this), this.reject.bind(this));
        } catch (error) {
            this.reject(error)
        }
    }

    resolve(result) {
        if (this.PromiseState === myPromise.PENDING) {
            setTimeout(() => {
                this.PromiseState = myPromise.FULFILLED;
                this.PromiseResult = result;
                this.onFulfilledCallbacks.forEach(callback => {
                    callback(result)
                })
            });
        }
    }

    reject(reason) {
        if (this.PromiseState === myPromise.PENDING) {
            setTimeout(() => {
                this.PromiseState = myPromise.REJECTED;
                this.PromiseResult = reason;
                this.onRejectedCallbacks.forEach(callback => {
                    callback(reason)
                })
            });
        }
    }

    /**
     * [注册fulfilled状态/rejected状态对应的回调函数] 
     * @param {function} onFulfilled  fulfilled状态时 执行的函数
     * @param {function} onRejected  rejected状态时 执行的函数 
     * @returns {function} newPromsie  返回一个新的promise对象
     */
    then(onFulfilled, onRejected) {
        onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
        onRejected = typeof onRejected === 'function' ? onRejected : reason => {
            throw reason;
        };

        let promise2 = new myPromise((resolve, reject) => {
            if (this.PromiseState === myPromise.FULFILLED) {
                setTimeout(() => {
                    try {
                        let x = onFulfilled(this.PromiseResult);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e);
                    }
                });
            } else if (this.PromiseState === myPromise.REJECTED) {
                setTimeout(() => {
                    try {
                        let x = onRejected(this.PromiseResult);
                        resolvePromise(promise2, x, resolve, reject);
                    } catch (e) {
                        reject(e)
                    }
                });
            } else if (this.PromiseState === myPromise.PENDING) {
                this.onFulfilledCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onFulfilled(this.PromiseResult);
                            resolvePromise(promise2, x, resolve, reject)
                        } catch (e) {
                            reject(e);
                        }
                    });
                });
                this.onRejectedCallbacks.push(() => {
                    setTimeout(() => {
                        try {
                            let x = onRejected(this.PromiseResult);
                            resolvePromise(promise2, x, resolve, reject);
                        } catch (e) {
                            reject(e);
                        }
                    });
                });
            }
        })

        return promise2
    }

    /**
     * Promise.resolve()
     * @param {[type]} value 要解析为 Promise 对象的值 
     */
    static resolve(value) {
        // 如果这个值是一个 promise ,那么将返回这个 promise 
        if (value instanceof myPromise) {
            return value;
        } else if (value instanceof Object && 'then' in value) {
            // 如果这个值是thenable(即带有`"then" `方法),返回的promise会“跟随”这个thenable的对象,采用它的最终状态;
            return new myPromise((resolve, reject) => {
                value.then(resolve, reject);
            })
        }

        // 否则返回的promise将以此值完成,即以此值执行`resolve()`方法 (状态为fulfilled)
        return new myPromise((resolve) => {
            resolve(value)
        })
    }

    /**
     * Promise.reject()
     * @param {*} reason 表示Promise被拒绝的原因
     * @returns 
     */
    static reject(reason) {
        return new myPromise((resolve, reject) => {
            reject(reason);
        })
    }

    /**
     * Promise.prototype.catch()
     * @param {*} onRejected 
     * @returns 
     */
    catch (onRejected) {
        return this.then(undefined, onRejected)
    }

    /**
     * Promise.prototype.finally()
     * @param {*} callBack 无论结果是fulfilled或者是rejected,都会执行的回调函数
     * @returns 
     */
    finally(callBack) {
        return this.then(callBack, callBack)
    }

    /**
     * Promise.all()
     * @param {iterable} promises 一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入
     * @returns 
     */
    static all(promises) {
        return new myPromise((resolve, reject) => {
            // 参数校验
            if (Array.isArray(promises)) {
                let result = []; // 存储结果
                let count = 0; // 计数器

                // 如果传入的参数是一个空的可迭代对象,则返回一个已完成(already resolved)状态的 Promise
                if (promises.length === 0) {
                    return resolve(promises);
                }

                promises.forEach((item, index) => {
                    //  判断参数是否为promise
                    if (item instanceof myPromise) {
                        myPromise.resolve(item).then(
                            value => {
                                count++;
                                // 每个promise执行的结果存储在result中
                                result[index] = value;
                                // Promise.all 等待所有都完成(或第一个失败)
                                count === promises.length && resolve(result);
                            },
                            reason => {
                                /**
                                 * 如果传入的 promise 中有一个失败(rejected),
                                 * Promise.all 异步地将失败的那个结果给失败状态的回调函数,而不管其它 promise 是否完成
                                 */
                                reject(reason);
                            }
                        )
                    } else {
                        // 参数里中非Promise值,原样返回在数组里
                        count++;
                        result[index] = item;
                        count === promises.length && resolve(result);
                    }
                })
            } else {
                return reject(new TypeError('Argument is not iterable'))
            }
        })
    }

    /**
     * Promise.allSettled()
     * @param {iterable} promises 一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入
     * @returns 
     */
    static allSettled(promises) {
        return new myPromise((resolve, reject) => {
            // 参数校验
            if (Array.isArray(promises)) {
                let result = []; // 存储结果
                let count = 0; // 计数器

                // 如果传入的是一个空数组,那么就直接返回一个resolved的空数组promise对象
                if (promises.length === 0) return resolve(promises);

                promises.forEach((item, index) => {
                    // 非promise值,通过Promise.resolve转换为promise进行统一处理
                    myPromise.resolve(item).then(
                        value => {
                            count++;
                            // 对于每个结果对象,都有一个 status 字符串。如果它的值为 fulfilled,则结果对象上存在一个 value 。
                            result[index] = {
                                status: 'fulfilled',
                                value
                            }
                            // 所有给定的promise都已经fulfilled或rejected后,返回这个promise
                            count === promises.length && resolve(result);
                        },
                        reason => {
                            count++;
                            /**
                             * 对于每个结果对象,都有一个 status 字符串。如果值为 rejected,则存在一个 reason 。
                             * value(或 reason )反映了每个 promise 决议(或拒绝)的值。
                             */
                            result[index] = {
                                status: 'rejected',
                                reason
                            }
                            // 所有给定的promise都已经fulfilled或rejected后,返回这个promise
                            count === promises.length && resolve(result);
                        }
                    )
                })
            } else {
                return reject(new TypeError('Argument is not iterable'))
            }
        })
    }

    /**
     * Promise.any()
     * @param {iterable} promises 一个promise的iterable类型(注:Array,Map,Set都属于ES6的iterable类型)的输入
     * @returns 
     */
    static any(promises) {
        return new myPromise((resolve, reject) => {
            // 参数校验
            if (Array.isArray(promises)) {
                let errors = []; // 
                let count = 0; // 计数器

                // 如果传入的参数是一个空的可迭代对象,则返回一个 已失败(already rejected) 状态的 Promise。
                if (promises.length === 0) return reject(new AggregateError([], 'All promises were rejected'));

                promises.forEach(item => {
                    // 非Promise值,通过Promise.resolve转换为Promise
                    myPromise.resolve(item).then(
                        value => {
                            // 只要其中的一个 promise 成功,就返回那个已经成功的 promise 
                            resolve(value);
                        },
                        reason => {
                            count++;
                            errors.push(reason);
                            /**
                             * 如果可迭代对象中没有一个 promise 成功,就返回一个失败的 promise 和AggregateError类型的实例,
                             * AggregateError是 Error 的一个子类,用于把单一的错误集合在一起。
                             */
                            count === promises.length && reject(new AggregateError(errors, 'All promises were rejected'));
                        }
                    )
                })
            } else {
                return reject(new TypeError('Argument is not iterable'))
            }
        })
    }

    /**
     * Promise.race()
     * @param {iterable} promises 可迭代对象,类似Array。详见 iterable。
     * @returns 
     */
    static race(promises) {
        return new myPromise((resolve, reject) => {
            // 参数校验
            if (Array.isArray(promises)) {
                // 如果传入的迭代promises是空的,则返回的 promise 将永远等待。
                if (promises.length > 0) {
                    promises.forEach(item => {
                        /**
                         * 如果迭代包含一个或多个非承诺值和/或已解决/拒绝的承诺,
                         * 则 Promise.race 将解析为迭代中找到的第一个值。
                         */
                        myPromise.resolve(item).then(resolve, reject);
                    })
                }
            } else {
                return reject(new TypeError('Argument is not iterable'))
            }
        })
    }
}

/**
 * 对resolve()、reject() 进行改造增强 针对resolve()和reject()中不同值情况 进行处理
 * @param  {promise} promise2 promise1.then方法返回的新的promise对象
 * @param  {[type]} x         promise1中onFulfilled或onRejected的返回值
 * @param  {[type]} resolve   promise2的resolve方法
 * @param  {[type]} reject    promise2的reject方法
 */
function resolvePromise(promise2, x, resolve, reject) {
    if (x === promise2) {
        return reject(new TypeError('Chaining cycle detected for promise'));
    }

    if (x instanceof myPromise) {
        if (x.PromiseState === myPromise.PENDING) {
            x.then(y => {
                resolvePromise(promise2, y, resolve, reject)
            }, reject);
        } else if (x.PromiseState === myPromise.FULFILLED) {
            resolve(x.PromiseResult);
        } else if (x.PromiseState === myPromise.REJECTED) {
            reject(x.PromiseResult);
        }
    } else if (x !== null && ((typeof x === 'object' || (typeof x === 'function')))) {
        try {
            var then = x.then;
        } catch (e) {
            return reject(e);
        }

        if (typeof then === 'function') {
            let called = false;
            try {
                then.call(
                    x,
                    y => {
                        if (called) return;
                        called = true;
                        resolvePromise(promise2, y, resolve, reject);
                    },
                    r => {
                        if (called) return;
                        called = true;
                        reject(r);
                    }
                )
            } catch (e) {
                if (called) return;
                called = true;

                reject(e);
            }
        } else {
            resolve(x);
        }
    } else {
        return resolve(x);
    }
}

myPromise.deferred = function () {
    let result = {};
    result.promise = new myPromise((resolve, reject) => {
        result.resolve = resolve;
        result.reject = reject;
    });
    return result;
}

module.exports = myPromise;

❤️ 结尾

如果这篇文章 对你的学习 有所 帮助,欢迎 点赞 👍 收藏留言 📝 ,你的支持 是我 创作分享动力!

关注公众号「前端圆圆」,第一时间获取最新的文章更新。
在这里插入图片描述
更多更全更详细优质内容猛戳这里查看
在这里插入图片描述

参考

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Promise.all() 方法接收一个 Promise 对象的数组作为参数,返回一个新的 Promise 对象。当数组中所有的 Promise 对象都成功时,新 Promise 对象才成功;只要任何一个 Promise 对象失败,新 Promise 对象就失败。 以下是一个手写Promise.all() 方法的实现: ```javascript function promiseAll(promises) { return new Promise((resolve, reject) => { let results = new Array(promises.length); let resolvedCount = 0; promises.forEach((promise, index) => { promise.then(result => { results[index] = result; resolvedCount++; if (resolvedCount === promises.length) { resolve(results); } }).catch(error => { reject(error); }); }); }); } ``` 这个实现中,我们首先创建了一个新的 Promise 对象,并在该 Promise 对象中使用一个数组来存储每个 Promise 对象的结果。我们还定义了一个计数器 resolvedCount,用于跟踪已经解决的 Promise 对象的数量。 然后,我们使用 forEach() 方法遍历传递给 promiseAll() 方法的 Promise 对象数组,并为每个 Promise 对象设置一个 then() 方法来处理成功的情况。在每个成功的 then() 回调函数中,我们将该 Promise 对象的结果存储在结果数组中,并将 resolvedCount 计数器增加 1。如果 resolvedCount 的值等于 Promise 对象的数组的长度,则意味着所有 Promise 对象都已成功解决,我们就可以使用 resolve() 方法来解决新的 Promise 对象,并传递结果数组作为参数。 如果任何一个 Promise 对象失败,则我们使用 catch() 方法处理错误情况,并使用 reject() 方法拒绝新的 Promise 对象。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值