Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JavaScript 深入系列之 Promise 全部实例方法和静态方法的模拟实现,包括处于 TC39 第四阶段草案的 Promise.any() #126

Open
yuanyuanbyte opened this issue Dec 21, 2021 · 0 comments

Comments

@yuanyuanbyte
Copy link
Owner

yuanyuanbyte commented Dec 21, 2021

本系列的主题是 JavaScript 深入系列,每期讲解一个技术要点。如果你还不了解各系列内容,文末点击查看全部文章,点我跳转到文末

如果觉得本系列不错,欢迎 Star,你的支持是我创作分享的最大动力。

微信图片_20211213171519

前言

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

上篇文章在这里:JavaScript 深入系列之 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"

输出结果:

在这里插入图片描述

测试通过 👏👏👏

2021年12月30日 更新

Byron_Yan 同学 在2021年12月30日留言:

promise.all实现,我发现入参数组元素是含有 then 属性的对象或者函数,不会原样返回;我觉得不用判断是否是 promise 的实例了,myPromise.resolve 的处理就可以了

我按照所给的用例跑了一下,测试代码如下:

const myPromsie = require('../myPromiseFully')

const p1 = Promise.resolve(3);
const p2 = {
    then: function (onFulfill) {
        onFulfill('then函数')
    }
}
const p3 = 42;

Promise.all([p1, p2, p3]).then(result => {
    console.log('原生 all fulfilled :>> ', result);
}, reason => {
    console.log('原生 all rejected :>> ', reason)
})

myPromsie.all([p1, p2, p3]).then(result => {
    console.log('手写 all fulfilled :>> ', result);
}, reason => {
    console.log('手写 all rejected :>> ', reason)
})

经测试确实存在上述问题,我们修改一下逻辑:

class myPromise {
    ...
    /**
     * 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与thenable对象
+                   if (item instanceof myPromise || (value instanceof Object && 'then' in value)) {
                        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;

再执行一下前面的测试用例:

image.png

测试通过

下面我们对上面代码做一下优化

对thenable对象的处理其实借鉴了myPromise.resolve()的实现:

/**
 * 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.all方法的又恰恰用到了myPromise.resolve(),所以这样的写法其实很多余:

if (item instanceof myPromise || (value instanceof Object && 'then' in value)) {
    ...
} else {
    // 参数里中非Promise值,原样返回在数组里
    count++;
    result[index] = item;
    count === promises.length && resolve(result);
}

这里直接用myPromsie.resolve才是最优解(至少现在我认为是)

Promsie.all 最终版的实现如下:

/**
 * 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) => {
                // myPromise.resolve方法中已经判断了参数是否为promise与thenable对象,所以无需在该方法中再次判断
                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 {
            return reject(new TypeError('Argument is not iterable'))
        }
    })
}

该写法通过了测试👏👏👏:

image

感谢Byron_Yan 同学的留言 👍👍👍

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;

❤️ 结尾

更多更全更详细优质内容猛戳这里查看

在这里插入图片描述

参考

查看原文

查看全部文章

博文系列目录

  • JavaScript 深入系列
  • JavaScript 专题系列
  • JavaScript 基础系列
  • 网络系列
  • 浏览器系列
  • Webpack 系列
  • Vue 系列
  • 性能优化与网络安全系列
  • HTML 应知应会系列
  • CSS 应知应会系列

交流

各系列文章汇总:https://github.com/yuanyuanbyte/Blog

我是圆圆,一名深耕于前端开发的攻城狮。

weixin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant