手寫(xiě) Promise 全部 API 教程
前言
我們?cè)?上篇文章?用了很大功夫?qū)崿F(xiàn)了 Promise 的核心方法,并且通過(guò)了 Promises/A+ 官方872個(gè)測(cè)試用例測(cè)試,接下來(lái)實(shí)現(xiàn)這些靜態(tài)方法已經(jīng)是小菜一碟了,因?yàn)檫@些 API 全部是對(duì)前面的封裝而已。
上篇文章在這里:手把手一行一行代碼教你 "手寫(xiě) Promise",完美通過(guò) Promises/A+ 官方872個(gè)測(cè)試用例
官方 Promise 還有很多API?,除了 then 方法以外還有?兩個(gè)實(shí)例方法:
Promise.prototype.catch
Promise.prototype.finally
? 以及目前 Promise 規(guī)范的?六個(gè)靜態(tài)方法:
Promise.resolve
Promise.reject
Promise.all
Promise.allSettled
Promise.any
Promise.race

雖然這些都不在 Promise/A+ 規(guī)范里面,但是我們也來(lái)實(shí)現(xiàn)一下吧,加深理解。其實(shí)我們前面我們用了很大功夫?qū)崿F(xiàn)了 Promise/A+ ,現(xiàn)在再來(lái)實(shí)現(xiàn)這些已經(jīng)是小菜一碟了,因?yàn)檫@些API全部是前面的封裝而已。
1. 實(shí)現(xiàn) Promise.resolve
Promise.resolve(value)?將給定的一個(gè)值轉(zhuǎn)為Promise對(duì)象。
如果這個(gè)值是一個(gè) promise ,那么將返回這個(gè) promise ;
如果這個(gè)值是thenable(即帶有
"then"?方法),返回的promise會(huì)“跟隨”這個(gè)thenable的對(duì)象,采用它的最終狀態(tài);否則返回的promise將以此值完成,即以此值執(zhí)行
resolve()方法 (狀態(tài)為fulfilled)。
根據(jù)規(guī)范我們這樣實(shí)現(xiàn)(寫(xiě)法一):
class myPromise {
...
}
function resolvePromise(promise2, x, resolve, reject) {
...
}
/**
* Promise.resolve()
* @param {[type]} value 要解析為 Promise 對(duì)象的值
*/
+ myPromise.resolve = function (value) {
+ // 如果這個(gè)值是一個(gè) promise ,那么將返回這個(gè) promise
+ if (value instanceof myPromise) {
+ return value;
+ } else if (value instanceof Object && 'then' in value) {
+ // 如果這個(gè)值是thenable(即帶有`"then" `方法),返回的promise會(huì)“跟隨”這個(gè)thenable的對(duì)象,采用它的最終狀態(tài);
+ return new myPromise((resolve, reject) => {
+ value.then(resolve, reject);
+ })
+ }
+
+ // 否則返回的promise將以此值完成,即以此值執(zhí)行`resolve()`方法 (狀態(tài)為fulfilled)
+ return new myPromise((resolve) => {
+ resolve(value)
+ })
+ }
module.exports = myPromise;
使用官方例子測(cè)試一下:
const myPromise = require('./promiseOtherAPI');
const promise1 = myPromise.resolve(123);
promise1.then((value) => {
console.log(value);
// expected output: 123
});
// Resolve一個(gè)thenable對(duì)象
var p1 = myPromise.resolve({
then: function (onFulfill) {
onFulfill("Resolving");
}
});
console.log(p1 instanceof myPromise) // true, 這是一個(gè)Promise對(duì)象
setTimeout(() => {
console.log('p1 :>> ', p1);
}, 1000);
p1.then(function (v) {
console.log(v); // 輸出"fulfilled!"
}, function (e) {
// 不會(huì)被調(diào)用
});
// Thenable在callback之前拋出異常
// myPromise rejects
var thenable = {
then: function (resolve) {
throw new TypeError("Throwing");
resolve("Resolving");
}
};
var p2 = myPromise.resolve(thenable);
p2.then(function (v) {
// 不會(huì)被調(diào)用
}, function (e) {
console.log(e); // TypeError: Throwing
});
輸出結(jié)果:
true
123
Resolving
TypeError: Throwing
p1 :>> myPromise {PromiseState: 'fulfilled', PromiseResult: 'Resolving', onFulfilledCallbacks: Array(1), onRejectedCallbacks: Array(1)}

測(cè)試通過(guò) ?
靜態(tài)方法改造
類(lèi)(class)通過(guò) static 關(guān)鍵字定義靜態(tài)方法。不能在類(lèi)的實(shí)例上調(diào)用靜態(tài)方法,而應(yīng)該通過(guò)類(lèi)本身調(diào)用。這些通常是實(shí)用程序方法,例如創(chuàng)建或克隆對(duì)象的功能。
類(lèi)相當(dāng)于實(shí)例的原型,所有在類(lèi)中定義的方法,都會(huì)被實(shí)例繼承。如果在一個(gè)方法前,加上static關(guān)鍵字,就表示該方法不會(huì)被實(shí)例繼承,而是直接通過(guò)類(lèi)來(lái)調(diào)用,這就稱(chēng)為“靜態(tài)方法”。
寫(xiě)法二、使用靜態(tài)方法 static:
class myPromise {
...
resolve(result) {
...
}
reject(reason) {
...
}
then(onFulfilled, onRejected) {
...
}
/**
* Promise.resolve()
* @param {[type]} value 要解析為 Promise 對(duì)象的值
*/
+ static resolve(value) {
+ // 如果這個(gè)值是一個(gè) promise ,那么將返回這個(gè) promise
+ if (value instanceof myPromise) {
+ return value;
+ } else if (value instanceof Object && 'then' in value) {
+ // 如果這個(gè)值是thenable(即帶有`"then" `方法),返回的promise會(huì)“跟隨”這個(gè)thenable的對(duì)象,采用它的最終狀態(tài);
+ return new myPromise((resolve, reject) => {
+ value.then(resolve, reject);
+ })
+ }
+
+ // 否則返回的promise將以此值完成,即以此值執(zhí)行`resolve()`方法 (狀態(tài)為fulfilled)
+ return new myPromise((resolve) => {
+ resolve(value)
+ })
+ }
}
function resolvePromise(promise2, x, resolve, reject) {
...
}
module.exports = myPromise;
2. 實(shí)現(xiàn) Promise.reject
Promise.reject()方法返回一個(gè)帶有拒絕原因的Promise對(duì)象。
官方例子:
Promise.reject(new Error('fail')).then(function() {
// not called
}, function(error) {
console.error(error); // Stacktrace
});
輸出結(jié)果:

根據(jù)規(guī)范我們這樣實(shí)現(xiàn)(寫(xiě)法一):
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;
使用官方用例測(cè)試一下:
const myPromise = require('./promiseOtherAPI')
myPromise.reject(new Error('fail')).then(function () {
// not called
}, function (error) {
console.error(error); // Error: fail
});
輸出結(jié)果:
Error: fail

測(cè)試通過(guò) ?
寫(xiě)法二、使用靜態(tài)方法 static:
class myPromise {
...
resolve(result) {
...
}
reject(reason) {
...
}
then(onFulfilled, onRejected) {
...
}
/**
* Promise.resolve()
* @param {[type]} value 要解析為 Promise 對(duì)象的值
*/
static resolve(value) {
// 如果這個(gè)值是一個(gè) promise ,那么將返回這個(gè) promise
if (value instanceof myPromise) {
return value;
} else if (value instanceof Object && 'then' in value) {
// 如果這個(gè)值是thenable(即帶有`"then" `方法),返回的promise會(huì)“跟隨”這個(gè)thenable的對(duì)象,采用它的最終狀態(tài);
return new myPromise((resolve, reject) => {
value.then(resolve, reject);
})
}
// 否則返回的promise將以此值完成,即以此值執(zhí)行`resolve()`方法 (狀態(tài)為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. 實(shí)現(xiàn) Promise.prototype.catch
catch()?方法返回一個(gè)Promise,并且處理拒絕的情況。它的行為與調(diào)用Promise.prototype.then(undefined, onRejected)?相同。
事實(shí)上, calling?obj.catch(onRejected)?內(nèi)部calls?obj.then(undefined, onRejected)。(這句話的意思是,我們顯式使用obj.catch(onRejected),內(nèi)部實(shí)際調(diào)用的是obj.then(undefined, onRejected))
Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的別名,用于指定發(fā)生錯(cuò)誤時(shí)的回調(diào)函數(shù)。
因此我們可以這樣來(lái)實(shí)現(xiàn):
class myPromise {
...
then(onFulfilled, onRejected) {
...
}
+ catch (onRejected) {
+ return this.then(undefined, onRejected)
+ }
}
function resolvePromise(promise2, x, resolve, reject) {
...
}
module.exports = myPromise;
就一行代碼,我的天,居然這么簡(jiǎn)單??
我們用官方例子來(lái)測(cè)試一下吧
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
測(cè)試通過(guò),沒(méi)毛病??
4. 實(shí)現(xiàn) Promise.prototype.finally
finally()?方法返回一個(gè)Promise。在promise結(jié)束時(shí),無(wú)論結(jié)果是fulfilled或者是rejected,都會(huì)執(zhí)行指定的回調(diào)函數(shù)。這為在Promise是否成功完成后都需要執(zhí)行的代碼提供了一種方式。
這避免了同樣的語(yǔ)句需要在then()和catch()中各寫(xiě)一次的情況。該方法是 ES2018 引入標(biāo)準(zhǔn)的。
由于無(wú)法知道promise的最終狀態(tài),所以finally的回調(diào)函數(shù)中不接收任何參數(shù),它僅用于無(wú)論最終結(jié)果如何都要執(zhí)行的情況。
根據(jù)規(guī)范我們這樣實(shí)現(xiàn):
class myPromise {
...
catch (onRejected) {
return this.then(undefined, onRejected)
}
/**
* finally
* @param {*} callBack 無(wú)論結(jié)果是fulfilled或者是rejected,都會(huì)執(zhí)行的回調(diào)函數(shù)
* @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;
對(duì),就這么簡(jiǎn)單 ?
測(cè)試一下:
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');
});
輸出結(jié)果:
1
finally
測(cè)試通過(guò) ??????
5. 實(shí)現(xiàn) Promise.all
Promise.all()?方法接收一個(gè)promise的iterable類(lèi)型(注:Array,Map,Set都屬于ES6的iterable類(lèi)型)的輸入,并且只返回一個(gè)Promise實(shí)例, 輸入的所有promise的resolve回調(diào)的結(jié)果是一個(gè)數(shù)組。
返回的這個(gè)Promise的resolve回調(diào)執(zhí)行是在所有輸入的promise的resolve回調(diào)都結(jié)束,或者輸入的iterable里沒(méi)有promise了的時(shí)候。它的reject回調(diào)執(zhí)行是,只要任何一個(gè)輸入的promise的reject回調(diào)執(zhí)行或者輸入不合法的promise就會(huì)立即拋出錯(cuò)誤,并且reject的是第一個(gè)拋出的錯(cuò)誤信息。
Promise.all?等待所有都完成(或第一個(gè)失?。?/p>如果傳入的參數(shù)是一個(gè)空的可迭代對(duì)象,則返回一個(gè)已完成(already resolved)狀態(tài)的 Promise
如果參數(shù)中包含非 promise 值,這些值將被忽略,但仍然會(huì)被放在返回?cái)?shù)組中,如果 promise 完成的話?
(也就是如果參數(shù)里的某值不是Promise,則需要原樣返回在數(shù)組里)在任何情況下,Promise.all 返回的 promise 的完成狀態(tài)的結(jié)果都是一個(gè)數(shù)組,它包含所有的傳入迭代參數(shù)對(duì)象的值(也包括非 promise 值)。
如果傳入的 promise 中有一個(gè)失?。╮ejected),Promise.all 異步地將失敗的那個(gè)結(jié)果給失敗狀態(tài)的回調(diào)函數(shù),而不管其它 promise 是否完成
根據(jù)規(guī)范我們這樣實(shí)現(xiàn):
class myPromise {
...
resolve(result) {
...
}
reject(reason) {
...
}
then(onFulfilled, onRejected) {
...
}
static resolve(value) {
...
}
static reject(reason) {
...
}
catch (onRejected) {
...
}
finally(callBack) {
...
}
/**
* Promise.all
* @param {iterable} promises 一個(gè)promise的iterable類(lèi)型(注:Array,Map,Set都屬于ES6的iterable類(lèi)型)的輸入
* @returns
*/
+ static all(promises) {
+ return new myPromise((resolve, reject) => {
+ // 參數(shù)校驗(yàn)
+ if (Array.isArray(promises)) {
+ let result = []; // 存儲(chǔ)結(jié)果
+ let count = 0; // 計(jì)數(shù)器
+
+ // 如果傳入的參數(shù)是一個(gè)空的可迭代對(duì)象,則返回一個(gè)已完成(already resolved)狀態(tài)的 Promise
+ if (promises.length === 0) {
+ return resolve(promises);
+ }
+
+ promises.forEach((item, index) => {
+ // 判斷參數(shù)是否為promise
+ if (item instanceof myPromise) {
+ myPromise.resolve(item).then(
+ value => {
+ count++;
+ // 每個(gè)promise執(zhí)行的結(jié)果存儲(chǔ)在result中
+ result[index] = value;
+ // Promise.all 等待所有都完成(或第一個(gè)失?。?/span>
+ count === promises.length && resolve(result);
+ },
+ reason => {
+ /**
+ * 如果傳入的 promise 中有一個(gè)失?。╮ejected),
+ * Promise.all 異步地將失敗的那個(gè)結(jié)果給失敗狀態(tài)的回調(diào)函數(shù),而不管其它 promise 是否完成
+ */
+ reject(reason);
+ }
+ )
+ } else {
+ // 參數(shù)里中非Promise值,原樣返回在數(shù)組里
+ 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;
使用官方例子測(cè)試一下:
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"]
輸出結(jié)果:
(3) [3, 42, 'foo']

測(cè)試通過(guò) ??????
測(cè)試 Promise.all 的快速返回失敗行為
Promise.all 在任意一個(gè)傳入的 promise 失敗時(shí)返回失敗。例如,如果你傳入的 promise中,有四個(gè) promise 在一定的時(shí)間之后調(diào)用成功函數(shù),有一個(gè)立即調(diào)用失敗函數(shù),那么 Promise.all 將立即變?yōu)槭 ?/p>
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"
輸出結(jié)果:

測(cè)試通過(guò) ??????
6. 實(shí)現(xiàn) Promise.allSettled
Promise.allSettled(iterable)方法返回一個(gè)在所有給定的promise都已經(jīng)fulfilled或rejected后的promise,并帶有一個(gè)對(duì)象數(shù)組,每個(gè)對(duì)象表示對(duì)應(yīng)的promise結(jié)果。
當(dāng)你有多個(gè)彼此不依賴(lài)的異步任務(wù)成功完成時(shí),或者你總是想知道每個(gè)promise的結(jié)果時(shí),通常使用它。
相比之下,Promise.all() 更適合彼此相互依賴(lài)或者在其中任何一個(gè)reject時(shí)立即結(jié)束。
參數(shù) iterable 是一個(gè)可迭代的對(duì)象,例如Array,其中每個(gè)成員都是Promise。
對(duì)于每個(gè)結(jié)果對(duì)象,都有一個(gè) status 字符串。如果它的值為 fulfilled,則結(jié)果對(duì)象上存在一個(gè) value 。如果值為 rejected,則存在一個(gè) reason 。value(或 reason )反映了每個(gè) promise 決議(或拒絕)的值。
在實(shí)現(xiàn)前我們需要驗(yàn)證一點(diǎn),輸入的非promise值應(yīng)該怎么處理?
為此我們?cè)?Promise.allSettled(iterable)?的參數(shù) iterable 中傳入一個(gè)非promise值,看一下 Promise.allSettled() 方法內(nèi)部會(huì)怎么處理:
const promise1 = Promise.resolve(3);
const promise2 = 1;
const promises = [promise1, promise2];
Promise.allSettled(promises).
then((results) => results.forEach((result) => console.log(result)));
輸出結(jié)果:
{status: 'fulfilled', value: 3}
{status: 'fulfilled', value: 1}

我們發(fā)現(xiàn) Promise.allSettled() 方法內(nèi)部將非 Promise 值轉(zhuǎn)換成 Promise 了
那下面我們就將非 Promise 值通過(guò) Promise.resolve 轉(zhuǎn)換為 Promise 進(jìn)行統(tǒng)一處理
根據(jù)規(guī)范我們這樣實(shí)現(xiàn):
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 一個(gè)promise的iterable類(lèi)型(注:Array,Map,Set都屬于ES6的iterable類(lèi)型)的輸入
* @returns
*/
+ static allSettled(promises) {
+ return new myPromise((resolve, reject) => {
+ // 參數(shù)校驗(yàn)
+ if (Array.isArray(promises)) {
+ let result = []; // 存儲(chǔ)結(jié)果
+ let count = 0; // 計(jì)數(shù)器
+
+ // 如果傳入的是一個(gè)空數(shù)組,那么就直接返回一個(gè)resolved的空數(shù)組promise對(duì)象
+ if (promises.length === 0) return resolve(promises);
+
+ promises.forEach((item, index) => {
+ // 非promise值,通過(guò)Promise.resolve轉(zhuǎn)換為promise進(jìn)行統(tǒng)一處理
+ myPromise.resolve(item).then(
+ value => {
+ count++;
+ // 對(duì)于每個(gè)結(jié)果對(duì)象,都有一個(gè) status 字符串。如果它的值為 fulfilled,則結(jié)果對(duì)象上存在一個(gè) value 。
+ result[index] = {
+ status: 'fulfilled',
+ value
+ }
+ // 所有給定的promise都已經(jīng)fulfilled或rejected后,返回這個(gè)promise
+ count === promises.length && resolve(result);
+ },
+ reason => {
+ count++;
+ /**
+ * 對(duì)于每個(gè)結(jié)果對(duì)象,都有一個(gè) status 字符串。如果值為 rejected,則存在一個(gè) reason 。
+ * value(或 reason )反映了每個(gè) promise 決議(或拒絕)的值。
+ */
+ result[index] = {
+ status: 'rejected',
+ reason
+ }
+ // 所有給定的promise都已經(jīng)fulfilled或rejected后,返回這個(gè)promise
+ count === promises.length && resolve(result);
+ }
+ )
+ })
+ } else {
+ return reject(new TypeError('Argument is not iterable'))
+ }
+ })
+ }
}
function resolvePromise(promise2, x, resolve, reject) {
...
}
module.exports = myPromise;
使用官方例子測(cè)試一下:
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))
輸出結(jié)果:
(0) []
{status: 'fulfilled', value: 3}
{status: 'fulfilled', value: 1}
{status: 'fulfilled', value: 3}
{status: 'rejected', reason: 'foo'}

測(cè)試通過(guò) perfect ???
7. 實(shí)現(xiàn) Promise.any
本質(zhì)上,這個(gè)方法和Promise.all()是相反的。
Promise.any()?接收一個(gè)Promise可迭代對(duì)象,只要其中的一個(gè) promise 成功,就返回那個(gè)已經(jīng)成功的 promise 。
如果可迭代對(duì)象中沒(méi)有一個(gè) promise 成功(即所有的 promises 都失敗/拒絕),就返回一個(gè)失敗的 promise 和AggregateError類(lèi)型的實(shí)例,它是 Error 的一個(gè)子類(lèi),用于把單一的錯(cuò)誤集合在一起。
如果傳入的參數(shù)是一個(gè)空的可迭代對(duì)象,則返回一個(gè) 已失?。╝lready rejected) 狀態(tài)的 Promise。
如果傳入的參數(shù)不包含任何 promise,則返回一個(gè) 異步完成 (asynchronously resolved)的 Promise。
(即將非Promise值,轉(zhuǎn)換為Promise并當(dāng)做成功)只要傳入的迭代對(duì)象中的任何一個(gè) promise 變成成功(resolve)狀態(tài),或者其中的所有的 promises 都失敗,那么返回的 promise 就會(huì) 異步地(當(dāng)調(diào)用棧為空時(shí)) 變成成功/失?。╮esolved/reject)狀態(tài)。
(如果所有Promise都失敗,則報(bào)錯(cuò))
注意!Promise.any() 方法依然是實(shí)驗(yàn)性的,尚未被所有的瀏覽器完全支持。它當(dāng)前處于 TC39 第四階段草案(Stage 4)

在?node v14.15.4?版本下測(cè)試?Promise.any()?發(fā)現(xiàn)還沒(méi)有被支持:
Uncaught TypeError: Promise.any is not a function


既然是處于草案階段的實(shí)驗(yàn)性 API ,如果想要在各版本瀏覽器中兼容性使用,那實(shí)現(xiàn)?Promise.any()?就很有必要 ??
根據(jù)規(guī)范我們這樣實(shí)現(xiàn):
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 一個(gè)promise的iterable類(lèi)型(注:Array,Map,Set都屬于ES6的iterable類(lèi)型)的輸入
* @returns
*/
+ static any(promises) {
+ return new myPromise((resolve, reject) => {
+ // 參數(shù)校驗(yàn)
+ if (Array.isArray(promises)) {
+ let errors = []; //
+ let count = 0; // 計(jì)數(shù)器
+
+ // 如果傳入的參數(shù)是一個(gè)空的可迭代對(duì)象,則返回一個(gè) 已失?。╝lready rejected) 狀態(tài)的 Promise。
+ if (promises.length === 0) return reject(new AggregateError('All promises were rejected'));
+
+ promises.forEach(item => {
+ // 非Promise值,通過(guò)Promise.resolve轉(zhuǎn)換為Promise
+ myPromise.resolve(item).then(
+ value => {
+ // 只要其中的一個(gè) promise 成功,就返回那個(gè)已經(jīng)成功的 promise
+ resolve(value);
+ },
+ reason => {
+ cout++;
+ errors.push(reason);
+ /**
+ * 如果可迭代對(duì)象中沒(méi)有一個(gè) promise 成功,就返回一個(gè)失敗的 promise 和AggregateError類(lèi)型的實(shí)例,
+ * AggregateError是 Error 的一個(gè)子類(lèi),用于把單一的錯(cuò)誤集合在一起。
+ */
+ 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;
使用官方例子測(cè)試一下:

發(fā)現(xiàn)報(bào)錯(cuò)了,提示 AggregateErro 沒(méi)有定義,這里不是我們代碼的問(wèn)題,是因?yàn)檫@個(gè) AggregateErro ,node v14.15.4?版本沒(méi)有支持,按理說(shuō)這個(gè)版本已經(jīng)很高了,為什么還沒(méi)有支持呢?
和 Promise.any() 一樣,這個(gè) AggregateError 也是一個(gè)實(shí)驗(yàn)中的功能,處于Stage 3 Draft (第三階段草案):

我們通過(guò)升級(jí) node 版本來(lái)兼容這些處于草案階段的實(shí)驗(yàn)功能~
從?node v14.15.4?升級(jí)到最新的?node v16.13.0

再次驗(yàn)證即可通過(guò):

用其他用例測(cè)試一下該方法:
const myPromise = require('../myPromiseFully');
/**
* 驗(yàn)證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);
})
輸出結(jié)果:
AggregateError: All promises were rejected
AggregateError: All promises were rejected
很快完成
測(cè)試通過(guò) ??
8. 實(shí)現(xiàn) Promise.race()
Promise.race(iterable)?方法返回一個(gè) promise,一旦迭代器中的某個(gè)promise解決或拒絕,返回的 promise就會(huì)解決或拒絕。
一個(gè)待定的 Promise 只要給定的迭代中的一個(gè)promise解決或拒絕,就采用第一個(gè)promise的值作為它的返回值,從而異步地解析或拒絕(一旦堆棧為空)。
race?函數(shù)返回一個(gè)?Promise,它將與第一個(gè)傳遞的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失敗(rejects),這要取決于第一個(gè)完成的方式是兩個(gè)中的哪個(gè)。
如果傳的迭代是空的,則返回的 promise 將永遠(yuǎn)等待。
如果迭代包含一個(gè)或多個(gè)非承諾值和/或已解決/拒絕的承諾,則 Promise.race 將解析為迭代中找到的第一個(gè)值。
根據(jù)規(guī)范我們這樣實(shí)現(xiàn):
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 可迭代對(duì)象,類(lèi)似Array。詳見(jiàn) iterable。
* @returns
*/
+ static race(promises) {
+ return new myPromise((resolve, reject) => {
+ // 參數(shù)校驗(yàn)
+ if (Array.isArray(promises)) {
+ // 如果傳入的迭代promises是空的,則返回的 promise 將永遠(yuǎn)等待。
+ if (promises.length > 0) {
+ promises.forEach(item => {
+ /**
+ * 如果迭代包含一個(gè)或多個(gè)非承諾值和/或已解決/拒絕的承諾,
+ * 則 Promise.race 將解析為迭代中找到的第一個(gè)值。
+ */
+ myPromise.resolve(item).then(resolve, reject);
+ })
+ }
+ } else {
+ return reject(new TypeError('Argument is not iterable'))
+ }
+ })
+ }
}
function resolvePromise(promise2, x, resolve, reject) {
...
}
module.exports = myPromise;
最后測(cè)試一下代碼:
const myPromise = require('../myPromiseFully');
/**
* 驗(yàn)證Promise.race()方法
*/
// 數(shù)組全是非Promise值,測(cè)試通過(guò)
let p1 = myPromise.race([1, 3, 4]);
setTimeout(() => {
console.log('p1 :>> ', p1);
});
// 空數(shù)組,測(cè)試通過(guò)
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');
});
// // 數(shù)組里有非Promise值,測(cè)試通過(guò)
myPromise.race([p11, p22, 10]).then((value) => {
console.log('p3 :>> ', value);
// Both resolve, but p22 is faster
});
// expected output: 10
// 數(shù)組里有'已解決的Promise' 和 非Promise值 測(cè)試通過(guò)
let p12 = myPromise.resolve('已解決的Promise')
setTimeout(() => {
myPromise.race([p12, p22, 10]).then((value) => {
console.log('p4 :>> ', value);
});
// expected output:已解決的Promise
});
// Promise.race的一般情況 測(cè)試通過(guò)
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"
輸出結(jié)果為:
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
測(cè)試通過(guò) ??????
完整代碼
手寫(xiě) Promise 全部方法的完整版代碼較長(zhǎng),這里如果看不清楚的可以去我的GitHub上看:
手寫(xiě) Promise 核心原理,完整的 Promise/A+ 實(shí)現(xiàn),通過(guò)了 Promise/A+ 官方872個(gè)測(cè)試用例測(cè)試,根據(jù)規(guī)范實(shí)現(xiàn)了 ES6+ 的全部 API,包括處于 TC39 第四階段草案的 Promise.any()??https://github.com/yuanyuanbyte/Promise
/**
* 在 myPromise.js 基礎(chǔ)上,根據(jù)規(guī)范實(shí)現(xiàn)了 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)
})
});
}
}
/**
* [注冊(cè)fulfilled狀態(tài)/rejected狀態(tài)對(duì)應(yīng)的回調(diào)函數(shù)]
* @param {function} onFulfilled fulfilled狀態(tài)時(shí) 執(zhí)行的函數(shù)
* @param {function} onRejected rejected狀態(tài)時(shí) 執(zhí)行的函數(shù)
* @returns {function} newPromsie 返回一個(gè)新的promise對(duì)象
*/
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 對(duì)象的值
*/
static resolve(value) {
// 如果這個(gè)值是一個(gè) promise ,那么將返回這個(gè) promise
if (value instanceof myPromise) {
return value;
} else if (value instanceof Object && 'then' in value) {
// 如果這個(gè)值是thenable(即帶有`"then" `方法),返回的promise會(huì)“跟隨”這個(gè)thenable的對(duì)象,采用它的最終狀態(tài);
return new myPromise((resolve, reject) => {
value.then(resolve, reject);
})
}
// 否則返回的promise將以此值完成,即以此值執(zhí)行`resolve()`方法 (狀態(tài)為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 無(wú)論結(jié)果是fulfilled或者是rejected,都會(huì)執(zhí)行的回調(diào)函數(shù)
* @returns
*/
finally(callBack) {
return this.then(callBack, callBack)
}
/**
* Promise.all()
* @param {iterable} promises 一個(gè)promise的iterable類(lèi)型(注:Array,Map,Set都屬于ES6的iterable類(lèi)型)的輸入
* @returns
*/
static all(promises) {
return new myPromise((resolve, reject) => {
// 參數(shù)校驗(yàn)
if (Array.isArray(promises)) {
let result = []; // 存儲(chǔ)結(jié)果
let count = 0; // 計(jì)數(shù)器
// 如果傳入的參數(shù)是一個(gè)空的可迭代對(duì)象,則返回一個(gè)已完成(already resolved)狀態(tài)的 Promise
if (promises.length === 0) {
return resolve(promises);
}
promises.forEach((item, index) => {
// 判斷參數(shù)是否為promise
if (item instanceof myPromise) {
myPromise.resolve(item).then(
value => {
count++;
// 每個(gè)promise執(zhí)行的結(jié)果存儲(chǔ)在result中
result[index] = value;
// Promise.all 等待所有都完成(或第一個(gè)失?。?/span>
count === promises.length && resolve(result);
},
reason => {
/**
* 如果傳入的 promise 中有一個(gè)失?。╮ejected),
* Promise.all 異步地將失敗的那個(gè)結(jié)果給失敗狀態(tài)的回調(diào)函數(shù),而不管其它 promise 是否完成
*/
reject(reason);
}
)
} else {
// 參數(shù)里中非Promise值,原樣返回在數(shù)組里
count++;
result[index] = item;
count === promises.length && resolve(result);
}
})
} else {
return reject(new TypeError('Argument is not iterable'))
}
})
}
/**
* Promise.allSettled()
* @param {iterable} promises 一個(gè)promise的iterable類(lèi)型(注:Array,Map,Set都屬于ES6的iterable類(lèi)型)的輸入
* @returns
*/
static allSettled(promises) {
return new myPromise((resolve, reject) => {
// 參數(shù)校驗(yàn)
if (Array.isArray(promises)) {
let result = []; // 存儲(chǔ)結(jié)果
let count = 0; // 計(jì)數(shù)器
// 如果傳入的是一個(gè)空數(shù)組,那么就直接返回一個(gè)resolved的空數(shù)組promise對(duì)象
if (promises.length === 0) return resolve(promises);
promises.forEach((item, index) => {
// 非promise值,通過(guò)Promise.resolve轉(zhuǎn)換為promise進(jìn)行統(tǒng)一處理
myPromise.resolve(item).then(
value => {
count++;
// 對(duì)于每個(gè)結(jié)果對(duì)象,都有一個(gè) status 字符串。如果它的值為 fulfilled,則結(jié)果對(duì)象上存在一個(gè) value 。
result[index] = {
status: 'fulfilled',
value
}
// 所有給定的promise都已經(jīng)fulfilled或rejected后,返回這個(gè)promise
count === promises.length && resolve(result);
},
reason => {
count++;
/**
* 對(duì)于每個(gè)結(jié)果對(duì)象,都有一個(gè) status 字符串。如果值為 rejected,則存在一個(gè) reason 。
* value(或 reason )反映了每個(gè) promise 決議(或拒絕)的值。
*/
result[index] = {
status: 'rejected',
reason
}
// 所有給定的promise都已經(jīng)fulfilled或rejected后,返回這個(gè)promise
count === promises.length && resolve(result);
}
)
})
} else {
return reject(new TypeError('Argument is not iterable'))
}
})
}
/**
* Promise.any()
* @param {iterable} promises 一個(gè)promise的iterable類(lèi)型(注:Array,Map,Set都屬于ES6的iterable類(lèi)型)的輸入
* @returns
*/
static any(promises) {
return new myPromise((resolve, reject) => {
// 參數(shù)校驗(yàn)
if (Array.isArray(promises)) {
let errors = []; //
let count = 0; // 計(jì)數(shù)器
// 如果傳入的參數(shù)是一個(gè)空的可迭代對(duì)象,則返回一個(gè) 已失敗(already rejected) 狀態(tài)的 Promise。
if (promises.length === 0) return reject(new AggregateError([], 'All promises were rejected'));
promises.forEach(item => {
// 非Promise值,通過(guò)Promise.resolve轉(zhuǎn)換為Promise
myPromise.resolve(item).then(
value => {
// 只要其中的一個(gè) promise 成功,就返回那個(gè)已經(jīng)成功的 promise
resolve(value);
},
reason => {
count++;
errors.push(reason);
/**
* 如果可迭代對(duì)象中沒(méi)有一個(gè) promise 成功,就返回一個(gè)失敗的 promise 和AggregateError類(lèi)型的實(shí)例,
* AggregateError是 Error 的一個(gè)子類(lèi),用于把單一的錯(cuò)誤集合在一起。
*/
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 可迭代對(duì)象,類(lèi)似Array。詳見(jiàn) iterable。
* @returns
*/
static race(promises) {
return new myPromise((resolve, reject) => {
// 參數(shù)校驗(yàn)
if (Array.isArray(promises)) {
// 如果傳入的迭代promises是空的,則返回的 promise 將永遠(yuǎn)等待。
if (promises.length > 0) {
promises.forEach(item => {
/**
* 如果迭代包含一個(gè)或多個(gè)非承諾值和/或已解決/拒絕的承諾,
* 則 Promise.race 將解析為迭代中找到的第一個(gè)值。
*/
myPromise.resolve(item).then(resolve, reject);
})
}
} else {
return reject(new TypeError('Argument is not iterable'))
}
})
}
}
/**
* 對(duì)resolve()、reject() 進(jìn)行改造增強(qiáng) 針對(duì)resolve()和reject()中不同值情況 進(jìn)行處理
* @param {promise} promise2 promise1.then方法返回的新的promise對(duì)象
* @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;
?? 結(jié)尾
如果這篇文章?對(duì)你的學(xué)習(xí)?有所?幫助,歡迎?點(diǎn)贊????收藏???留言??? ,你的支持?是我?創(chuàng)作分享?的?動(dòng)力!
關(guān)注公眾號(hào)「前端圓圓」,第一時(shí)間獲取最新的文章更新。
更多更全更詳細(xì)?的?優(yōu)質(zhì)內(nèi)容,?猛戳這里查看

參考
????????鏈接放在最下面了
Promises/A+ (promisesaplus.com)
Promise A+ 規(guī)范 (malcolmyu.github.io)
手寫(xiě)Promise核心代碼
Promise詳解與實(shí)現(xiàn)(Promise/A+規(guī)范)
手寫(xiě)一個(gè)Promise/A+,完美通過(guò)官方872個(gè)測(cè)試用例
剖析Promise內(nèi)部結(jié)構(gòu),一步一步實(shí)現(xiàn)一個(gè)完整的、能通過(guò)所有Test case的Promise類(lèi)
Promise.prototype.then() - JavaScript | MDN (mozilla.org)
Promise.prototype.catch() - JavaScript | MDN (mozilla.org)
從一道讓我失眠的 Promise 面試題開(kāi)始,深入分析 Promise 實(shí)現(xiàn)細(xì)節(jié)
手寫(xiě)Promise - 常用靜態(tài)方法all、resolve、reject、race
手寫(xiě)Promise - 實(shí)例方法catch、finally
手寫(xiě)Promise周邊方法
Promise.any 的作用,如何自己實(shí)現(xiàn)一個(gè) Promise.any
參考文章鏈接
https://promisesaplus.com
https://malcolmyu.github.io/2015/06/12/Promises-A-Plus
https://www.bilibili.com/video/BV1RR4y1p7my?from=search&seid=14552865939751412953
https://www.jianshu.com/p/459a856c476f
https://www.cnblogs.com/dennisj/p/12660388.html
https://github.com/xieranmaya/blog/issues/3
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch
https://juejin.cn/post/6945319439772434469#heading-19
https://segmentfault.com/a/1190000023379900
https://segmentfault.com/a/1190000023279746
https://zhuanlan.zhihu.com/p/143699690
https://zhuanlan.zhihu.com/p/374679042
