前言
手寫 Promise 是面試的時候大家都逃避的送命題,在學些瞭解後發現通過實現源碼更能將新一代的異步方案理解的通透,知其然知其所以然的運用。
如果直接將源碼貼到此處勢必不能有更大的收穫,下面就按實現版本來看做簡要分析。
回顧 Promise
Promise 是 CommonJS 提出來的這一種規範,有多個版本,在 ES6 當中已經納入規範,原生支持 Promise 對象,非 ES6 環境可以用類似 Bluebird、Q 這類庫來支持。
Promise 可以將回調變成鏈式調用寫法,流程更加清晰,代碼更加優雅,還可以批量處理異步任務。
簡單歸納下 Promise:三個狀態、兩個過程、一個方法,快速記憶方法:3-2-1
三個狀態:pending、fulfilled、rejected
兩個過程:
- pending → fulfilled(resolve)
- pending → rejected(reject)
一個方法:then
當然還有其他概念,如 catch、 Promise.all/race/allSettled。
基礎版
基礎測試用例
// 1. 鏈式調用
var p1 = new Promise(function (resolve, reject) {
console.log("init Promise");
if (Math.random() > 0.5) {
resolve("大");
} else {
reject("小");
}
});
p1.then(
(data) => console.log("success", data),
(reason) => console.log("error", reason)
).then(
() => console.log("success 2"),
() => console.log("error 2")
);
// 2. 異步延時
var sleep = (time, data) =>
new Promise(function (resolve, reject) {
setTimeout(resolve, time, data);
});
sleep(3000, "時間到!").then((val) => {
console.log(val);
});
// 3. 狀態變更後不可變
const p2 = new Promise(function (resolve, reject) {
resolve("失敗了!");
reject("還會成功嗎!");
});
p2.then(
(data) => console.log(data),
(reason) => console.log(reason)
);
// Promise 打印日誌:
// init Promise
// success 大 / error 小
// 失敗了!
// success 2 / error 2
// 時間到!(延時 3 s)
Promise 的基本特徵
- new promise 時, 需要傳入一個立即執行函數 fn,fn 接受兩個參數,分別是 resolve 和 reject;
- promise 有三個狀態:pending,fulfilled,or rejected, 默認狀態是 pending,只能從 pending 到 rejected, 或者從 pending 到 fulfilled,狀態一旦確認,就不會再改變;
- promise 必須有一個 then 方法,then 接收兩個參數,分別是 promise 成功的回調 fulfilledFn, 和 promise 失敗的回調 rejectedFn;
- promise then 支持鏈式調用。
手寫基礎版
class Promise {
constructor(executor) {
this.status = "pending";
this.handleFulfilled = []; // 存儲成功後的回調
this.handleRejection = []; // 存儲失敗後的回調
// ! resolve 形參的實際參數在這兒
const resolve = (data) => {
// 狀態變更只有一次
if (this.status !== "pending") {
return;
}
this.status = "fulfilled";
// ! 等一會,否則 handleFulfilled 為空
setTimeout(() => {
this.handleFulfilled.forEach((fn) => fn(data));
}, 0);
};
const reject = (reason) => {
if (this.status !== "pending") {
return;
}
this.status = "rejected";
setTimeout(() => {
this.handleRejection.forEach((fn) => fn(reason));
}, 0);
};
try {
executor(resolve, reject);
} catch (e) {
// 遇到錯誤時,捕獲錯誤,執行 reject 函數
reject(e);
}
}
then(fulfilledFn, rejectedFn) {
this.handleFulfilled.push(fulfilledFn);
this.handleRejection.push(rejectedFn);
return this;
}
}
測試用例:
// 簡易版 Promise 打印日誌:
// init Promise
// success 大 / error 小
// success 2 / error 2 (x 未通過)
// 失敗了!
// 時間到!(延時 3 s)
存在的問題:
- 微任務宏任務隊列打印順序
- then 鏈式調用不是通過返回 this,而是返回一個新的 promise
- 鏈式調用支持參數缺省
- 等等...
認識 Promise /A+ 規範
手寫 Promise,需要遵守怎樣的規則,業界所有 Promise 的類庫都遵循 Promise/A+ 規範。譯文
改進版
先按 Promise 的基本規範對上面的基礎版進行改進。
- new promise 時, 需要傳入一個立即執行函數
executor,executor接受兩個參數,分別是 resolve 和 reject; - promise 有三個狀態:
pending,fulfilled,orrejected, 默認狀態是pending,只能從pending到rejected, 或者從pending到fulfilled,狀態一旦確認,就不會再改變;「規範 Promise/A+ 2.1」 - promise 有一個
value保存成功狀態的值,可以是undefined/thenable/promise;「規範 Promise/A+ 1.3」 - promise 有一個
reason保存失敗狀態的值;「規範 Promise/A+ 1.5」 - promise 必須有一個 then 方法,then 接收兩個參數,分別是 promise 成功的回調 onFulfilled, 和 promise 失敗的回調 onRejected;「規範 Promise/A+ 2.2」
- 如果調用 then 時,promise 已經成功,則執行
onFulfilled,參數是 promise 的value; - 如果調用 then 時,promise 已經失敗,那麼執行
onRejected, 參數是 promise 的reason;
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";
class Promise {
constructor(executor) {
this.status = PENDING; // 默認狀態為 PENGING
this.value = undefined; // 存放成功狀態得值
this.reason = undefined; // 存放失敗狀態得值
this.handleFulfilled = []; // 存儲成功後的回調
this.handleRejection = []; // 存儲失敗後的回調
// ! resolve 形參的實際參數在這兒
const resolve = (data) => {
// 狀態變更只有一次
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = data;
this.handleFulfilled.forEach((fn) => fn(data));
}
};
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.handleRejection.forEach((fn) => fn(reason));
}
};
try {
executor(resolve, reject);
} catch (e) {
// 遇到錯誤時,捕獲錯誤,執行 reject 函數
reject(e);
}
}
then(onFulfilled, onRejected) {
if (this.status === FULFILLED) {
onFulfilled(this.value);
}
if (this.status === REJECTED) {
onRejected(this.reason);
}
if (this.status === PENDING) {
this.handleFulfilled.push(() => onFulfilled(this.value));
this.handleRejection.push(() => onRejected(this.reason));
}
return this;
}
}
Promise A+規範版
規範思路梳理
如果調用 then 時,promise 已經成功,則執行 onFulfilled,並將 promise 的值作為參數傳遞進去。 如果 promise 已經失敗,那麼執行 onRejected, 並將 promise 失敗的原因作為參數傳遞進去。如果 promise 的狀態是 pending,需要將 onFulfilled 和 onRejected 函數存放起來,等待狀態確定後,再依次將對應的函數執行(發佈訂閲)
- then 的參數
onFulfilled和onRejected可以缺省,如果onFulfilled或者onRejected不是函數,將其忽略,且依舊可以在下面的then中獲取到之前返回的值;「規範 Promise/A+ 2.2.1、2.2.1.1、2.2.1.2」 - promise 可以 then 多次,promise 的 then 方法返回一個新的 promise 「規範 Promise/A+ 2.2.7」
- 如果 then 返回值 x 是一個普通值,那麼就會把這個結果作為參數,傳遞給下一個 then 的成功的回調(onFulfilled)
- 如果 then 中拋出了異常,那麼就會把這個異常作為參數,傳遞給下一個 then 的失敗的回調(onRejected) 規範 Promise/A+ 2.2.7.2」
- 如果 then 的返回值 x 是一個 promise,那麼會等這個 promise 執行完,promise 如果成功,就走下一個 then 的成功;如果失敗,就走下一個 then 的失敗;如果拋出異常,就走下一個 then 的失敗;「規範 Promise/A+ 2.2.7.3、2.2.7.4」
- 如果 then 的返回值 x 和 promise 是同一個引用對象,造成循環引用,則拋出異常,把異常傳遞給下一個 then 的失敗的回調中;「規範 Promise/A+ 2.3.1」
- 如果 then 的返回值 x 是一個 promise,且 x 同時調用 resolve 函數和 reject 函數,則第一次調用優先,其他所有調用被忽略;「規範 Promise/A+ 2.3.3.3.3」
源碼實現
const PENDING = "PENDING";
const FULFILLED = "FULFILLED";
const REJECTED = "REJECTED";
// 將 onFufilled 的返回值進行判斷取值處理,把最後獲得的普通值放入最外面那層的 Promise 的 resolve 函數中
const resolvePromise = (promise2, x, resolve, reject) => {
// 自己等待自己完成是錯誤的實現,用一個循環引用的類型錯誤,結束掉 promise Promise/A+ 2.3.1
if (promise2 === x) {
return reject(
new TypeError("Chaining cycle detected for promise #<Promise>")
);
}
// 只能調用一次,為了判斷resolve過的就不用再reject了,(比如有reject和resolve的時候)Promise/A+ 2.3.3.3.3
let called;
// 如果 x 不是null,是對象或者方法
if ((typeof x === "object" && x != null) || typeof x === "function") {
try {
// 這個首先存儲對 x.then 的引用,然後測試該引用
let then = x.then;
if (typeof then === "function") {
// 那我們就認為他是promise,call他,因為then方法中的this來自自己的promise對象
// 不要寫成 x.then,直接 then.call 就可以了 因為 x.then 會再次取值,Object.defineProperty Promise/A+ 2.3.3.3
// 第一個參數是將x這個promise方法作為this指向,後兩個參數分別為成功失敗回調
then.call(
x,
(y) => {
// 根據 promise 的狀態決定是成功還是失敗
if (called) return;
called = true;
// 遞歸解析的過程(因為可能 promise 中還有 promise) Promise/A+ 2.3.3.3.1
resolvePromise(promise2, y, resolve, reject);
},
(r) => {
// 只要失敗就失敗 Promise/A+ 2.3.3.3.2
if (called) return;
called = true;
reject(r);
}
);
} else {
// 如果 x.then 是個普通值就直接返回 resolve 作為結果 Promise/A+ 2.3.3.4
resolve(x);
}
} catch (e) {
// Promise/A+ 2.3.3.2
if (called) return;
called = true;
reject(e);
}
} else {
// 如果 x 是個普通值就直接返回 resolve 作為結果 Promise/A+ 2.3.4
resolve(x);
}
};
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach((fn) => fn());
}
};
let reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach((fn) => fn());
}
};
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
//解決 onFufilled,onRejected 沒有傳值的問題
//Promise/A+ 2.2.1 / Promise/A+ 2.2.5 / Promise/A+ 2.2.7.3 / Promise/A+ 2.2.7.4
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (v) => v;
//因為錯誤的值要讓後面訪問到,所以這裏也要拋出個錯誤,不然會在之後 then 的 resolve 中捕獲
onRejected =
typeof onRejected === "function"
? onRejected
: (err) => {
throw err;
};
// 每次調用 then 都返回一個新的 promise Promise/A+ 2.2.7
let promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
//Promise/A+ 2.2.2
//Promise/A+ 2.2.4 --- setTimeout 宏任務模擬異步
setTimeout(() => {
try {
//Promise/A+ 2.2.7.1
// 因為有的時候需要判斷then中的方法是否返回一個promise對象,所以需要判斷
// 如果返回值為promise對象,則需要取出結果當作promise2的resolve結果
// 如果不是,直接作為promise2的resolve結果
let x = onFulfilled(this.value);
// x可能是一個proimise
// 抽離出一個公共方法來判斷他們是否為promise對象
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
//Promise/A+ 2.2.7.2
reject(e);
}
}, 0);
}
if (this.status === REJECTED) {
//Promise/A+ 2.2.3
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
}
測試一下,打印日誌也對了:
// Promise 打印日誌:
// init Promise
// error 小
// 失敗了!
// success 2
// 時間到!(延時 3 s)
再試試 then 的鏈式調用和值的穿透:
const promise = new Promise((resolve, reject) => {
reject("失敗");
})
.then()
.then()
.then(
(data) => {
console.log(data);
},
(err) => {
console.log("[Error]:", err); //log: [Error]: 失敗
}
);
完善 Promise API
雖然上述的 promise 源碼已經符合 Promise/A+ 的規範,但是原生的 Promise 還提供了一些其他方法,如:
Promise.prototype.catch()Promise.prototype.finally()Promise.resolve(value)—— 使用給定 value 創建一個 resolved 的 promise。Promise.reject(error)—— 使用給定 error 創建一個 rejected 的 promise。Promise.all(promises)—— 等待所有 promise 都 resolve 時,返回存放它們結果的數組。如果給定的任意一個 promise 為 reject,那麼它就會變成 Promise.all 的 error,所有其他 promise 的結果都會被忽略。Promise.race(promises)—— 等待第一個 settle 的 promise,並將其 result/error 作為結果返回。Promise.any(promises)(ES2021 新增方法)—— 等待第一個 fulfilled 的 promise,並將其結果作為結果返回。如果所有 promise 都 rejected,Promise.any 則會拋出 AggregateError 錯誤類型的 error 實例。-
Promise.allSettled(promises)(ES2020 新增方法)—— 等待所有 promise 都 settle 時,並以包含以下內容的對象數組的形式返回它們的結果:- status: "fulfilled" 或 "rejected"
- value(如果 fulfilled)或 reason(如果 rejected)。
下面具體説一下每個方法的實現:
Promise.prototype.catch
先從錯誤捕獲開始,catch 用來捕獲 promise 的異常,就相當於一個沒有成功的 then。
Promise.prototype.catch = function (errCallback) {
return this.then(undefined, errCallback);
};
測試例子:
// 拋出一個錯誤,大多數時候將調用catch方法
var p1 = new Promise(function (resolve, reject) {
throw "Uh-oh!";
});
p1.catch(function (e) {
console.log(e); // "Uh-oh!"
});
// 在異步函數中拋出的錯誤不會被catch捕獲到
var p2 = new Promise(function (resolve, reject) {
setTimeout(function () {
throw "Uncaught Exception!";
}, 1000);
});
p2.catch(function (e) {
console.log(e); // 不會執行
});
Promise.prototype.finally()
finally 方法,在 promise 結束時,無論結果是 fulfilled 或者是 rejected,都會執行指定的回調函數。這為在 Promise 是否成功完成後都需要執行的代碼提供了一種方式。
這避免了同樣的語句需要在 then()和 catch()中各寫一次的情況。
Promise.prototype.finally = function (callback) {
return this.then(
(value) => {
return Promise.resolve(callback(value)).then(() => value);
},
(reason) => {
return Promise.resolve(callback(reason)).then(() => {
throw reason;
});
}
);
};
測試用例:
const promise1 = Promise.resolve(123);
promise1
.then((value) => {
return value;
})
.finally((res) => {
console.log("finally", res); // finally 123
});
Promise.resolve(456)
.finally(() => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(123);
}, 3000);
});
})
.then((data) => {
console.log(data, "success"); // 3秒後,456 'success'
})
.catch((err) => {
console.log(err, "error");
});
Promise.resolve
默認產生一個成功的 promise。Promise.resolve(value)方法返回一個以給定值解析後的 Promise 對象。如果這個值是一個 promise ,那麼將返回這個 promise ;如果這個值是 thenable(即帶有"then" 方法),返回的 promise 會“跟隨”這個 thenable 的對象,採用它的最終狀態;否則返回的 promise 將以此值完成。此函數將類 promise 對象的多層嵌套展平。
static resolve(data){
return new Promise((resolve,reject)=>{
resolve(data);
})
}
如果參數是 promise 會等待這個 promise 解析完畢,在向下執行,所以這裏需要在 constructor 方法中做一個小小的處理:
let reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach((fn) => fn());
}
};
let resolve = (value) => {
// 如果 value 是一個promise,那我們的庫中應該也要實現一個遞歸解析
if (value instanceof Promise) {
// 遞歸解析
return value.then(resolve, reject);
}
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach((fn) => fn());
}
};
測試例子:
const promise1 = Promise.resolve(123);
promise1.then((value) => {
console.log(value);
// expected output: 123
});
Promise.resolve(
new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
resolve("ok");
} else {
reject("err");
}
}, 3000);
})
)
.then((data) => {
console.log(data, "success"); // ok success
})
.catch((err) => {
console.log(err, "error"); // err error
});
Promise.reject()
默認產生一個失敗的 promise,Promise.reject 是直接將值變成錯誤結果。
static reject(reason){
return new Promise((resolve,reject)=>{
reject(reason);
})
}
Promise.all()
promise.all 是解決併發問題的,多個異步併發獲取最終的結果(如果有一個失敗則失敗)。
Promise.all = function (values) {
if (!Array.isArray(values)) {
const type = typeof values;
return new TypeError(`TypeError: ${type} ${values} is not iterable`);
}
return new Promise((resolve, reject) => {
let resultArr = [];
let orderIndex = 0;
const processResultByKey = (value, index) => {
resultArr[index] = value;
if (++orderIndex === values.length) {
resolve(resultArr);
}
};
for (let i = 0; i < values.length; i++) {
let value = values[i];
if (value && typeof value.then === "function") {
value.then((value) => {
processResultByKey(value, i);
}, reject);
} else {
processResultByKey(value, i);
}
}
});
};
測試一下:
Promise.all([
new Promise((resolve) => setTimeout(() => resolve(1), 3000)), // 1
new Promise((resolve) => setTimeout(() => resolve(2), 2000)), // 2
new Promise((resolve) => setTimeout(() => resolve(3), 1000)), // 3
]).then(alert); // 1,2,3 當上面這些 promise 準備好時:每個 promise 都貢獻了數組中的一個元素
Promise.all([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) =>
setTimeout(() => reject(new Error("Whoops!")), 2000)
),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)),
]).catch(alert); // Error: Whoops!
Promise.race()
Promise.race 用來處理多個請求,採用最快的(誰先完成用誰的)。
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
// 一起執行就是for循環
for (let i = 0; i < promises.length; i++) {
let val = promises[i];
if (val && typeof val.then === "function") {
val.then(resolve, reject);
} else {
// 普通值
resolve(val);
}
}
});
};
這裏第一個 promise 最快,所以它變成了結果。第一個 settled 的 promise “贏得了比賽”之後,所有進一步的 result/error 都會被忽略。
例如,這裏的結果將是 1:
Promise.race([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) =>
setTimeout(() => reject(new Error("Whoops!")), 2000)
),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)),
]).then(alert); // 1
Promise.any()
與 Promise.race 類似,區別在於 Promise.any 只等待第一個 fulfilled 的 promise,並將這個 fulfilled 的 promise 返回,它不會等待其他的 promise 全部完成。如果給出的 promise 都 rejected,那麼則返回 rejected 的 promise 和 AggregateError 錯誤類型的 error 實例—— 一個特殊的 error 對象,在其 errors 屬性中存儲着所有 promise error。
例如,這裏的結果將是 1:
Promise.any([
new Promise((resolve, reject) =>
setTimeout(() => reject(new Error("Whoops!")), 1000)
),
new Promise((resolve, reject) => setTimeout(() => resolve(1), 2000)),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)),
]).then(alert); // 1
這裏的第一個 promise 是最快的,但 rejected 了,所以第二個 promise 則成為了結果。在第一個 fulfilled 的 promise “贏得比賽”後,所有進一步的結果都將被忽略。
這是一個所有 promise 都失敗的例子:
Promise.any([
new Promise((resolve, reject) =>
setTimeout(() => reject(new Error("Ouch!")), 1000)
),
new Promise((resolve, reject) =>
setTimeout(() => reject(new Error("Error!")), 2000)
),
]).catch((error) => {
console.log(error.constructor.name); // AggregateError
console.log(error.errors[0]); // Error: Ouch!
console.log(error.errors[1]); // Error: Error!
});
正如你所看到的,我們在 AggregateError 錯誤類型的 error 實例的 errors 屬性中可以訪問到失敗的 promise 的 error 對象。
實現該方法,可以通過上面的兩個例子:
Promise.any 只要傳入的 promise 有一個是 fullfilled 則立即 resolve 出去,否則將所有 reject 結果收集起來並返回 AggregateError
Promise.any = function (promises) {
return new Promise((resolve, reject) => {
promises = Array.isArray(promises) ? promises : [];
let len = promises.length;
// 用於收集所有 reject
let errs = [];
// 如果傳入的是一個空數組,那麼就直接返回 AggregateError
if (len === 0)
return reject(new AggregateError("All promises were rejected"));
promises.forEach((promise) => {
promise.then(
(value) => {
resolve(value);
},
(err) => {
len--;
errs.push(err);
if (len === 0) {
reject(new AggregateError(errs));
}
}
);
});
});
};
Promise.allSettled()
如果任意的 promise reject,則 Promise.all 整個將會 reject。當我們需要 所有 結果都成功時,它對這種“全有或全無”的情況很有用:Promise.allSettled 等待所有的 promise 都被 settle,無論結果如何。結果數組具有:
- {status:"fulfilled", value:result} 對於成功的響應,
- {status:"rejected", reason:error} 對於 error。
Promise.allSettled = function (promises) {
const rejectHandler = (reason) => ({ status: "rejected", reason });
const resolveHandler = (value) => ({ status: "fulfilled", value });
const convertedPromises = promises.map((p) =>
Promise.resolve(p).then(resolveHandler, rejectHandler)
);
return Promise.all(convertedPromises);
};
測試一下:
Promise.allSettled([
new Promise((resolve) => setTimeout(() => resolve(1), 3000)), // 1
new Promise((resolve) => setTimeout(() => resolve(2), 2000)), // 2
new Promise((resolve) => setTimeout(() => resolve(3), 1000)), // 3
]).then(console.log); // [{status: 'fulfilled', value: 1}, {status: 'fulfilled', value: 2}, {status: 'fulfilled', value: 3}]
Promise.allSettled([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) =>
setTimeout(() => reject(new Error("Whoops!")), 2000)
),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000)),
]).then(console.log); // [{status: 'fulfilled', value: 1}, {status: 'rejected', value: "Error: Whoops!"}, {status: 'fulfilled', value: 3}]測試一下:
參考文章
- MDN - Promise
- javascript.info - promise-api
- Promise A+ 規範
- 面試官:“你能手寫一個 Promise 嗎”
- Promise 的源碼實現(完美符合 Promise/A+規範)
- Promise 之你看得懂的 Promise
- Promise.any 的作用,如何自己實現一個 Promise.any
- 我以為我很懂Promise,直到我開始實現Promise/A+規範
- 八段代碼徹底掌握 Promise
- Promise 必知必會(十道題)