前端面試?這份手擼Promise請你收下
前言
現在很多大廠面試前端都會要求能夠手動的寫出一個Promise
,所以這裡整理了一份手寫的Promise
。
絕對詳細,功能絕對強大。如果你不瞭解Promise
的基本使用,那麼本篇文章可能不太適合你,如果你對Promise
有過一些瞭解,那麼這篇文章絕對是你進階的好幫手。
除開catch()
以及finally()
和allSettled
介面沒實現之外,其他的所有原生Promise
支援的功能此手寫的Promise
都支援。
書寫Promise
的難度其實就在於then()
方法的鏈式呼叫以及值穿透傳遞,其他的其實都還好。
讓這篇文章滾進你的收藏夾吧!
Promise狀態實現
在原生Promise
pending
:未解決狀態
fulfilled
:已解決狀態
rejected
:解決失敗狀態
所以第一步,要先實現這三種狀態。
並且在原生Promise
中具有value
用於記錄resolve()
與reject()
中的值用於傳遞給then()
方法。
class MyPromise { static PENDING = "pending"; static FUFILLED = "fulfilled"; static REJECTED = "rejected"; constructor(executor) { this.status = MyPromise.PENDING; // 初始為準備狀態 this.value = null; // 初始值 } }
Promise執行函式
原生Promise
的建構函式中會接收一個executor
引數,該引數當是一個函式。
用於同步的執行當前任務,當任務完成後應該具有resolve()
方法以及reject()
方法來通知then()
方法當前執行任務的執行狀態並傳遞值。
class MyPromise { static PENDING = "pending"; static FUFILLED = "fulfilled"; static REJECTED = "rejected"; constructor(executor) { this.status = MyPromise.PENDING; // 初始狀態為準備狀態 this.value = null; // 初始值 executor(this.resolve, this.reject); // 傳遞形參,執行executor函式 } resolve(value) { this.status = MyPromise.FUFILLED; this.value = value; } reject(reason) { this.status = MyPromise.REJECTED; this.value = reason; } }
上面這樣寫在執行resolve()
以及reject()
時會出現問題,原因是this
指向為undefiled
(嚴格模式)。
<script src="./Promise核心.js"></script>
<script>
"use strict";
let p1 = new MyPromise((resolve, reject) => {
resolve("成功")
})
console.log(p1);
</script>
這是由於我們在執行函式中呼叫了resolve()
與reject()
,故this
指向為executor
的函式上下文。
解決這個問題可以使用bind()
來改變this
指向。
class MyPromise {
static PENDING = "pending";
static FUFILLED = "fulfilled";
static REJECTED = "rejected";
constructor(executor) {
this.status = MyPromise.PENDING; // 初始狀態為準備狀態
this.value = null; // 初始值
executor(this.resolve.bind(this), this.reject.bind(this)); // 傳遞形參,執行executor函式
}
resolve(value) {
this.status = MyPromise.FUFILLED;
this.value = value;
}
reject(reason) {
this.status = MyPromise.REJECTED;
this.value = reason;
}
}
Promise狀態限制
當前Promise
狀態只應該改變一次而不能多次改變,顯然我們上面的程式碼不能做到這點限制。
<script src="./Promise核心.js"></script>
<script>
"use strict";
let p1 = new MyPromise((resolve, reject) => {
resolve("成功");
reject("失敗");
// 對於原生Promise來說,狀態只能改變一次。但是這裡卻允許兩次改變,故是有問題的
})
console.log(p1); // MyPromise {status: "rejected", value: "失敗"}
</script>
所以這裡要對程式碼加上限制。
class MyPromise {
static PENDING = "pending";
static FUFILLED = "fulfilled";
static REJECTED = "rejected";
constructor(executor) {
this.status = MyPromise.PENDING; // 初始狀態為準備狀態
this.value = null; // 初始值
executor(this.resolve.bind(this), this.reject.bind(this)); // 傳遞形參,執行executor函式
}
resolve(value) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.FUFILLED;
this.value = value;
}
}
reject(reason) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.REJECTED;
this.value = reason;
}
}
}
Promise執行異常
在執行函式executor
中可能引發異常,這會讓當前的Promise
的狀態改變為rejected
。
所以在上面程式碼基礎上需要加入try...catch
進行處理。
當then()
方法捕捉到執行函式executor
中的異常時,可以讓第二個引數的函式對其異常進行處理,但是我們目前還沒實現then()
,所以直接丟給reject()
即可,當實現then()
時自然會使用到reject()
中傳遞過來的值。
class MyPromise {
static PENDING = "pending";
static FUFILLED = "fulfilled";
static REJECTED = "rejected";
constructor(executor) {
this.status = MyPromise.PENDING; // 初始狀態為準備狀態
this.value = null; // 初始值
try {
executor(this.resolve.bind(this), this.reject.bind(this)); // 傳遞形參,執行executor函式
} catch (e) {
this.status = MyPromise.REJECTED; // 異常發生改變狀態
this.reject(e); // 記錄異常資訊
}
}
resolve(value) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.FUFILLED;
this.value = value;
}
}
reject(reason) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.REJECTED;
this.value = reason;
}
}
}
then方法基礎實現
原生的Promise
狀態改變後,可以執行其下的then()
方法,所以我們需要來封裝出一個then()
方法。
then()
方法接收兩個函式,第一個函式onFulfilled
用於處理上一個Promise
的fulfilled
狀態,第二個函式onRejected
用於處理上一個Promise
的rejected
狀態。
並且then()
方法中的這兩個函式都應該具有一個形參,用於接收到Promise
的resolve()
或reject()
中傳遞的值。
class MyPromise {
static PENDING = "pending";
static FUFILLED = "fulfilled";
static REJECTED = "rejected";
constructor(executor) {
this.status = MyPromise.PENDING; // 初始狀態為準備狀態
this.value = null; // 初始值
try {
executor(this.resolve.bind(this), this.reject.bind(this)); // 傳遞形參,執行executor函式
} catch (e) {
this.status = MyPromise.REJECTED; // 異常發生改變狀態
this.reject(e); // 記錄異常資訊
}
}
resolve(value) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.FUFILLED;
this.value = value;
}
}
reject(reason) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.REJECTED;
this.value = reason;
}
}
then(onFulfilled, onRejected) {
if(this.status == MyPromise.FUFILLED){ // 狀態改變時執行
onFulfilled(this.value);
}
if(this.status == MyPromise.REJECTED){ // 狀態改變時執行
onRejected(this.value);
}
}
}
then方法引數優化
上面已經說過,then()
方法具有兩個引數,這兩個引數分別對應兩個函式用來處理上一個Promsie
的resolve()
與reject()
。
但是在原生Promise
中,這兩個方法可以不進行傳遞,所以我們需要對上述程式碼進行優化。
當then()
方法中的某一個引數不為函式時,讓它自動建立一個空函式。
class MyPromise {
static PENDING = "pending";
static FUFILLED = "fulfilled";
static REJECTED = "rejected";
constructor(executor) {
this.status = MyPromise.PENDING; // 初始狀態為準備狀態
this.value = null; // 初始值
try {
executor(this.resolve.bind(this), this.reject.bind(this)); // 傳遞形參,執行executor函式
} catch (e) {
this.status = MyPromise.REJECTED; // 異常發生改變狀態
this.reject(e); // 記錄異常資訊
}
}
resolve(value) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.FUFILLED;
this.value = value;
}
}
reject(reason) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.REJECTED;
this.value = reason;
}
}
then(onFulfilled, onRejected) {
if(typeof onFulfilled != "function"){ // 如果傳入的不是一個函式,預設建立空函式
onFulfilled = ()=>{};
}
if(typeof onRejected != "function"){ // 如果傳入的不是一個函式,預設建立空函式
onRejected = ()=>{};
}
if(this.status == MyPromise.FUFILLED){ // 狀態改變時執行
onFulfilled(this.value);
}
if(this.status == MyPromise.REJECTED){ // 狀態改變時執行
onRejected(this.value);
}
}
}
then方法異常捕獲
當then()
方法中處理fulfilled
狀態的函式onFulfilled
或者處理rejected
狀態的函式onRejected
在執行時出現異常應該進行捕獲並且傳遞給下一個then()
的處理rejected
狀態的函式onRejected
。
這裡我們先讓所有的異常都交由當前then()
處理rejected
狀態的函式onRejected
,後面再進行優化。
class MyPromise {
static PENDING = "pending";
static FUFILLED = "fulfilled";
static REJECTED = "rejected";
constructor(executor) {
this.status = MyPromise.PENDING; // 初始狀態為準備狀態
this.value = null; // 初始值
try {
executor(this.resolve.bind(this), this.reject.bind(this)); // 傳遞形參,執行executor函式
} catch (e) {
this.status = MyPromise.REJECTED; // 異常發生改變狀態
this.reject(e); // 記錄異常資訊
}
}
resolve(value) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.FUFILLED;
this.value = value;
}
}
reject(reason) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.REJECTED;
this.value = reason;
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled != "function") { // 如果傳入的不是一個函式,預設建立空函式
onFulfilled = () => { };
}
if (typeof onRejected != "function") { // 如果傳入的不是一個函式,預設建立空函式
onRejected = () => { };
}
if (this.status == MyPromise.FUFILLED) { // 狀態改變時執行
try { // then處理成功的函式onFulfilled中出現異常,交由當前then處理失敗的函式onRejected函式進行處理。這個後面會做優化
onFulfilled(this.value);
} catch (e) {
onRejected(e);
}
}
if (this.status == MyPromise.REJECTED) { // 狀態改變時執行
try { // then處理失敗的函式onRejected中出現異常,交由當前then處理失敗的函式onRejected函式進行處理。這個後面會做優化
onRejected(this.value);
} catch (e) {
onRejected(e);
}
}
}
}
then方法非同步執行
在原生的Promise
中,executor
函式是同步執行的,而then()
方法是非同步執行故排在同步執行之後。
但是我們的Promise
卻沒有做到這一點,下面的實驗將說明這個問題
<script src="./Promise核心.js"></script>
<script>
"use strict";
let p1 = new MyPromise((resolve, reject) => {
reject("失敗");
}).then(
null,
error => {
console.log(error); // 先列印 失敗
}
)
console.log("hello,Promise"); // 後列印 hello,Promise
</script>
最簡單的解決方案就是為then()
中處理成功或處理失敗的函式執行外套上一個setTimeout
,讓其處理排線上程同步任務執行之後再進行執行。
class MyPromise {
static PENDING = "pending";
static FUFILLED = "fulfilled";
static REJECTED = "rejected";
constructor(executor) {
this.status = MyPromise.PENDING; // 初始狀態為準備狀態
this.value = null; // 初始值
try {
executor(this.resolve.bind(this), this.reject.bind(this)); // 傳遞形參,執行executor函式
} catch (e) {
this.status = MyPromise.REJECTED; // 異常發生改變狀態
this.reject(e); // 記錄異常資訊
}
}
resolve(value) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.FUFILLED;
this.value = value;
}
}
reject(reason) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.REJECTED;
this.value = reason;
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled != "function") { // 如果傳入的不是一個函式,預設建立空函式
onFulfilled = () => { };
}
if (typeof onRejected != "function") { // 如果傳入的不是一個函式,預設建立空函式
onRejected = () => { };
}
if (this.status == MyPromise.FUFILLED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try { // then處理成功的函式onFulfilled中出現異常,交由當前then處理失敗的函式onRejected函式進行處理。這個後面會做優化
onFulfilled(this.value);
} catch (e) {
onRejected(e);
}
})
}
if (this.status == MyPromise.REJECTED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try { // then處理失敗的函式onRejected中出現異常,交由當前then處理失敗的函式onRejected函式進行處理。這個後面會做優化
onRejected(this.value);
} catch (e) {
onRejected(e);
}
})
}
}
}
執行函式非同步阻塞
此時我們的程式碼仍然具有一個問題,即在執行函式executor
中使用setTimeout
時,下面的then()
會進行阻塞。
這是因為當前Promise
狀態是pending
而then()
方法中並沒有對pending
狀態進行處理的策略所導致的。
<script src="./Promise核心.js"></script>
<script>
"use strict";
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("成功"); // 同步任務執行完三秒後才會改變當前Promise狀態
}, 3000);
}).then((success) => { // 但是這裡先執行了then,Promise狀態為pending,故發生阻塞
console.log(success); // 阻塞了,不列印
})
</script>
既然當前Promise
狀態是pending
,3秒後狀態才發生改變,那麼我們就可以通過不斷的迴圈來看看它何時改變狀態。
所以第一步是定義一個執行非同步的陣列。然後再將then()
中處理正確的函式onFulfilled
與處理錯誤的函式onRejected
壓進去。
class MyPromise {
static PENDING = "pending";
static FUFILLED = "fulfilled";
static REJECTED = "rejected";
constructor(executor) {
this.status = MyPromise.PENDING; // 初始狀態為準備狀態
this.value = null; // 初始值
this.callbacks = []; // 如果是一個非同步操作,則放入該陣列中
try {
executor(this.resolve.bind(this), this.reject.bind(this)); // 傳遞形參,執行executor函式
} catch (e) {
this.status = MyPromise.REJECTED; // 異常發生改變狀態
this.reject(e); // 記錄異常資訊
}
}
resolve(value) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.FUFILLED;
this.value = value;
})
}
}
reject(reason) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.REJECTED;
this.value = reason;
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled != "function") { // 如果傳入的不是一個函式,預設建立空函式
onFulfilled = () => { };
}
if (typeof onRejected != "function") { // 如果傳入的不是一個函式,預設建立空函式
onRejected = () => { };
}
if (this.status == MyPromise.FUFILLED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try { // then處理成功的函式onFulfilled中出現異常,交由當前then處理失敗的函式onRejected函式進行處理。這個後面會做優化
onFulfilled(this.value);
} catch (e) {
onRejected(e);
}
})
}
if (this.status == MyPromise.REJECTED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try { // then處理失敗的函式onRejected中出現異常,交由當前then處理失敗的函式onRejected函式進行處理。這個後面會做優化
onRejected(this.value);
} catch (e) {
onRejected(e);
}
})
}
if (this.status == MyPromise.PENDING) { // 如果當前Promise是等待處理狀態,則將處理成功的函式與處理失敗的函式壓入非同步陣列。
this.callbacks.push({
onFulfilled,
onRejected,
});
}
}
}
當陣列壓入完成後,執行函式executor
會去呼叫resolve()
或者reject()
改變當前Promise
狀態。
所以我們還需要在resolve()
與reject()
方法中對非同步的陣列處理函式進行呼叫。
class MyPromise {
static PENDING = "pending";
static FUFILLED = "fulfilled";
static REJECTED = "rejected";
constructor(executor) {
this.status = MyPromise.PENDING; // 初始狀態為準備狀態
this.value = null; // 初始值
this.callbacks = []; // 如果是一個非同步操作,則放入該陣列中
try {
executor(this.resolve.bind(this), this.reject.bind(this)); // 傳遞形參,執行executor函式
} catch (e) {
this.status = MyPromise.REJECTED; // 異常發生改變狀態
this.reject(e); // 記錄異常資訊
}
}
resolve(value) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.FUFILLED;
this.value = value;
this.callbacks.map(callback => { // // 呼叫處理非同步executor裡resolve的方法。
callback.onFulfilled(value);
})
}
}
reject(reason) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.REJECTED;
this.value = reason;
this.callbacks.map(callback => { // 呼叫處理非同步executor裡reject的方法。
callback.onRejected(reason);
})
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled != "function") { // 如果傳入的不是一個函式,預設建立空函式
onFulfilled = () => { };
}
if (typeof onRejected != "function") { // 如果傳入的不是一個函式,預設建立空函式
onRejected = () => { };
}
if (this.status == MyPromise.FUFILLED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try { // then處理成功的函式onFulfilled中出現異常,交由當前then處理失敗的函式onRejected函式進行處理。這個後面會做優化
onFulfilled(this.value);
} catch (e) {
onRejected(e);
}
})
}
if (this.status == MyPromise.REJECTED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try { // then處理失敗的函式onRejected中出現異常,交由當前then處理失敗的函式onRejected函式進行處理。這個後面會做優化
onRejected(this.value);
} catch (e) {
onRejected(e);
}
})
}
if (this.status == MyPromise.PENDING) { // 如果當前Promise是等待處理狀態,則將處理成功的函式與處理失敗的函式壓入非同步陣列。
this.callbacks.push({
onFulfilled,
onRejected,
});
}
}
}
非同步執行函式的then異常捕獲
上面我們對同步執行函式executor
呼叫then()
方法中可能出現的異常進行了處理。
就是下面這一段程式碼。
if (this.status == MyPromise.FUFILLED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try { // then處理成功的函式onFulfilled中出現異常,交由當前then處理失敗的函式onRejected函式進行處理。這個後面會做優化
onFulfilled(this.value);
} catch (e) {
onRejected(e);
}
})
}
if (this.status == MyPromise.REJECTED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try { // then處理失敗的函式onRejected中出現異常,交由當前then處理失敗的函式onRejected函式進行處理。這個後面會做優化
onRejected(this.value);
} catch (e) {
onRejected(e);
}
})
}
但是我們還沒有對非同步執行函式executor
呼叫then()
方法中可能出現的異常進行處理。
if (this.status == MyPromise.PENDING) { // 如果當前Promise是等待處理狀態,則將處理成功的函式與處理失敗的函式壓入非同步陣列。
this.callbacks.push({
onFulfilled,
onRejected,
});
}
}
這會導致下面這樣的使用場景出現問題。
<script src="./Promise核心.js"></script>
<script>
"use strict";
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("成功");
}, 3000);
}).then((success) => {
throw new Error("自定義異常丟擲"); // 直接在處理成功狀態的函式onFulfilled中丟擲了異常,顯然是不符合原生Promise的
});
</script>
那麼我們就來加上異常捕獲即可,這裡還是先傳遞給當前then()
處理rejected
狀態的函式,後面會做修改。
因為原版Promise
會傳遞給下一個then()
中處理rejected
狀態的函式,而不是當前then()
。
class MyPromise {
static PENDING = "pending";
static FUFILLED = "fulfilled";
static REJECTED = "rejected";
constructor(executor) {
this.status = MyPromise.PENDING; // 初始狀態為準備狀態
this.value = null; // 初始值
this.callbacks = []; // 如果是一個非同步操作,則放入該陣列中
try {
executor(this.resolve.bind(this), this.reject.bind(this)); // 傳遞形參,執行executor函式
} catch (e) {
this.status = MyPromise.REJECTED; // 異常發生改變狀態
this.reject(e); // 記錄異常資訊
}
}
resolve(value) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.FUFILLED;
this.value = value;
this.callbacks.map(callback => { // // 呼叫處理非同步executor裡resolve的方法。
callback.onFulfilled(value);
})
}
}
reject(reason) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.REJECTED;
this.value = reason;
this.callbacks.map(callback => { // 呼叫處理非同步executor裡reject的方法。
callback.onRejected(reason);
})
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled != "function") { // 如果傳入的不是一個函式,預設建立空函式
onFulfilled = () => { };
}
if (typeof onRejected != "function") { // 如果傳入的不是一個函式,預設建立空函式
onRejected = () => { };
}
if (this.status == MyPromise.FUFILLED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try { // then處理成功的函式onFulfilled中出現異常,交由當前then處理失敗的函式onRejected函式進行處理。這個後面會做優化
onFulfilled(this.value);
} catch (e) {
onRejected(e);
}
})
}
if (this.status == MyPromise.REJECTED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try { // then處理失敗的函式onRejected中出現異常,交由當前then處理失敗的函式onRejected函式進行處理。這個後面會做優化
onRejected(this.value);
} catch (e) {
onRejected(e);
}
})
}
if (this.status == MyPromise.PENDING) { // 如果當前Promise是等待處理狀態,則將處理成功的函式與處理失敗的函式壓入非同步陣列。
this.callbacks.push({
onFulfilled: value => {
try { // 非同步executor改變狀態對其then中的onFulfilled進行異常捕獲
onFulfilled(value);
} catch (e) {
onRejected(e);
}
},
onRejected: value => {
try { // 非同步executor改變狀態對其then中的onRejected進行異常捕獲
onRejected(value);
} catch (e) {
onRejected(e);
}
}
});
}
}
}
then方法鏈式操作
對於原生的Promise
來講,每一個then()
最後返回的都是一個新的Promise
。所以才能達到支援不斷的then()
進行鏈式操作,所以我們也可以這樣做。
class MyPromise {
static PENDING = "pending";
static FUFILLED = "fulfilled";
static REJECTED = "rejected";
constructor(executor) {
this.status = MyPromise.PENDING; // 初始狀態為準備狀態
this.value = null; // 初始值
this.callbacks = []; // 如果是一個非同步操作,則放入該陣列中
try {
executor(this.resolve.bind(this), this.reject.bind(this)); // 傳遞形參,執行executor函式
} catch (e) {
this.status = MyPromise.REJECTED; // 異常發生改變狀態
this.reject(e); // 記錄異常資訊
}
}
resolve(value) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.FUFILLED;
this.value = value;
this.callbacks.map(callback => { // // 呼叫處理非同步executor裡resolve的方法。
callback.onFulfilled(value);
})
}
}
reject(reason) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.REJECTED;
this.value = reason;
this.callbacks.map(callback => { // 呼叫處理非同步executor裡reject的方法。
callback.onRejected(reason);
})
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled != "function") { // 如果傳入的不是一個函式,預設建立空函式
onFulfilled = () => { };
}
if (typeof onRejected != "function") { // 如果傳入的不是一個函式,預設建立空函式
onRejected = () => { };
}
return new MyPromise((resolve, reject) => { // 返回一個新的Promise
if (this.status == MyPromise.FUFILLED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try { // then處理成功的函式onFulfilled中出現異常,交由當前then處理失敗的函式onRejected函式進行處理。這個後面會做優化
onFulfilled(this.value);
} catch (e) {
onRejected(e);
}
})
}
if (this.status == MyPromise.REJECTED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try { // then處理失敗的函式onRejected中出現異常,交由當前then處理失敗的函式onRejected函式進行處理。這個後面會做優化
onRejected(this.value);
} catch (e) {
onRejected(e);
}
})
}
if (this.status == MyPromise.PENDING) { // 如果當前Promise是等待處理狀態,則將處理成功的函式與處理失敗的函式壓入非同步陣列。
this.callbacks.push({
onFulfilled: value => {
try { // 非同步executor改變狀態對其then中的onFulfilled進行異常捕獲
onFulfilled(value);
} catch (e) {
onRejected(e);
}
},
onRejected: value => {
try { // 非同步executor改變狀態對其then中的onRejected進行異常捕獲
onRejected(value);
} catch (e) {
onRejected(e);
}
}
});
}
});
}
}
現在我們的Promise
已經支援then()
的鏈式操作了,但是上面程式碼還是遺留了幾個問題。
1.
then()
還沒有返回值,返回普通值該怎麼處理,返回一個新的Promise
該怎麼處理2.沒有異常傳遞,原生
Promise
中的then()
當丟擲異常時應該進行捕獲並傳遞給下一個then()
3.不支援
then()
穿透4.不支援型別限制
接下來繼續對程式碼做出優化調整。
then中返回普通值
在原生的Promise
中每一個then()
所產生的Promise
預設狀態都是fulfilled
,如果當前then()
返回是一個值的話那麼下一個then()
將接受到該值。
這個也非常簡單,程式碼接收一下每一個onFulfilled()
與onRejected()
的返回值就好,並使用resolve()
改變狀態為fulfilled
以及將值進行傳遞給下一個then()
。
class MyPromise {
static PENDING = "pending";
static FUFILLED = "fulfilled";
static REJECTED = "rejected";
constructor(executor) {
this.status = MyPromise.PENDING; // 初始狀態為準備狀態
this.value = null; // 初始值
this.callbacks = []; // 如果是一個非同步操作,則放入該陣列中
try {
executor(this.resolve.bind(this), this.reject.bind(this)); // 傳遞形參,執行executor函式
} catch (e) {
this.status = MyPromise.REJECTED; // 異常發生改變狀態
this.reject(e); // 記錄異常資訊
}
}
resolve(value) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.FUFILLED;
this.value = value;
this.callbacks.map(callback => { // // 呼叫處理非同步executor裡resolve的方法。
callback.onFulfilled(value);
})
}
}
reject(reason) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.REJECTED;
this.value = reason;
this.callbacks.map(callback => { // 呼叫處理非同步executor裡reject的方法。
callback.onRejected(reason);
})
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled != "function") { // 如果傳入的不是一個函式,預設建立空函式
onFulfilled = () => { };
}
if (typeof onRejected != "function") { // 如果傳入的不是一個函式,預設建立空函式
onRejected = () => { };
}
return new MyPromise((resolve, reject) => { // 返回一個新的Promise
if (this.status == MyPromise.FUFILLED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try { // then處理成功的函式onFulfilled中出現異常,交由當前then處理失敗的函式onRejected函式進行處理。這個後面會做優化
let result = onFulfilled(this.value);
resolve(result);
} catch (e) {
onRejected(e);
}
})
}
if (this.status == MyPromise.REJECTED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try { // then處理失敗的函式onRejected中出現異常,交由當前then處理失敗的函式onRejected函式進行處理。這個後面會做優化
let result = onRejected(this.value);
resolve(result);
} catch (e) {
onRejected(e);
}
})
}
if (this.status == MyPromise.PENDING) { // 如果當前Promise是等待處理狀態,則將處理成功的函式與處理失敗的函式壓入非同步陣列。
this.callbacks.push({
onFulfilled: value => {
try { // 非同步executor改變狀態對其then中的onFulfilled進行異常捕獲
let result = onFulfilled(value);
resolve(result);
} catch (e) {
onRejected(e);
}
},
onRejected: value => {
try { // 非同步executor改變狀態對其then中的onRejected進行異常捕獲
let result = onRejected(value);
resolve(result);
} catch (e) {
onRejected(e);
}
}
});
}
});
}
}
這樣我們的then()
就支援返回普通值了。
<script src="./Promise核心.js"></script>
<script>
"use strict";
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("成功");
}, 3000);
}).then((success) => {
return "hello";
}).then((success)=>{
console.log(success); // hello
});
</script>
then中的異常傳遞
在上面的程式碼中,then()
方法裡的處理成功函式onFulfilled
以及處理失敗函式onRejected
在程式碼執行時丟擲的異常都會統一進行捕獲並且傳遞給當前then()
方法處理失敗的函式onRejected
。
這個與原生的Promise
有出入,對於原生Promise
來講應該是傳遞給下一個then()
進行處理而不是當前then()
。
改動也非常簡單,將原來發生異常傳遞的函式onRejected()
改為reject()
即可,這就是傳遞給下一個then()
。
class MyPromise {
static PENDING = "pending";
static FUFILLED = "fulfilled";
static REJECTED = "rejected";
constructor(executor) {
this.status = MyPromise.PENDING; // 初始狀態為準備狀態
this.value = null; // 初始值
this.callbacks = []; // 如果是一個非同步操作,則放入該陣列中
try {
executor(this.resolve.bind(this), this.reject.bind(this)); // 傳遞形參,執行executor函式
} catch (e) {
this.status = MyPromise.REJECTED; // 異常發生改變狀態
this.reject(e); // 記錄異常資訊
}
}
resolve(value) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.FUFILLED;
this.value = value;
this.callbacks.map(callback => { // // 呼叫處理非同步executor裡resolve的方法。
callback.onFulfilled(value);
})
}
}
reject(reason) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.REJECTED;
this.value = reason;
this.callbacks.map(callback => { // 呼叫處理非同步executor裡reject的方法。
callback.onRejected(reason);
})
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled != "function") { // 如果傳入的不是一個函式,預設建立空函式
onFulfilled = () => { };
}
if (typeof onRejected != "function") { // 如果傳入的不是一個函式,預設建立空函式
onRejected = () => { };
}
return new MyPromise((resolve, reject) => { // 返回一個新的Promise
if (this.status == MyPromise.FUFILLED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try {
let result = onFulfilled(this.value);
resolve(result);
} catch (e) {
reject(e); // 傳遞給下一個then
}
})
}
if (this.status == MyPromise.REJECTED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try {
let result = onRejected(this.value);
resolve(result);
} catch (e) {
reject(e); // 傳遞給下一個then
}
})
}
if (this.status == MyPromise.PENDING) { // 如果當前Promise是等待處理狀態,則將處理成功的函式與處理失敗的函式壓入非同步陣列。
this.callbacks.push({
onFulfilled: value => {
try {
let result = onFulfilled(value);
resolve(result);
} catch (e) {
reject(e); // 傳遞給下一個then
}
},
onRejected: value => {
try {
let result = onRejected(value);
resolve(result);
} catch (e) {
reject(e); // 傳遞給下一個then
}
}
});
}
});
}
}
<script src="./Promise核心.js"></script>
<script>
"use strict";
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve("成功");
}, 3000);
}).then((success) => {
throw new Error("新錯誤");
}).then(null, error => {
console.log(error); // 上一個then的錯誤成功由該then接收
});
</script>
then穿透功能實現
在原生的Promise
中是支援then()
的穿透傳值的。
<script>
"use strict";
new Promise((resolve, reject) => {
resolve("成功");
})
.then() // 穿透
.then(
success => {
console.log(success); // 成功
},
error => {
console.log(error);
})
</script>
但是我們的Promise
卻不支援。
<script src="./Promise核心.js"></script>
<script>
"use strict";
new MyPromise((resolve, reject) => {
resolve("成功");
})
.then() // 不支援穿透
.then(
success => {
console.log(success);
},
error => {
console.log(error);
})
</script>
原因在於如果沒有對then()
進行傳遞引數,那麼內部其實是會建立兩個空函式。
我們只需要在空函式內部返回this.value
即可。
class MyPromise {
static PENDING = "pending";
static FUFILLED = "fulfilled";
static REJECTED = "rejected";
constructor(executor) {
this.status = MyPromise.PENDING; // 初始狀態為準備狀態
this.value = null; // 初始值
this.callbacks = []; // 如果是一個非同步操作,則放入該陣列中
try {
executor(this.resolve.bind(this), this.reject.bind(this)); // 傳遞形參,執行executor函式
} catch (e) {
this.status = MyPromise.REJECTED; // 異常發生改變狀態
this.reject(e); // 記錄異常資訊
}
}
resolve(value) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.FUFILLED;
this.value = value;
this.callbacks.map(callback => { // // 呼叫處理非同步executor裡resolve的方法。
callback.onFulfilled(value);
})
}
}
reject(reason) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.REJECTED;
this.value = reason;
this.callbacks.map(callback => { // 呼叫處理非同步executor裡reject的方法。
callback.onRejected(reason);
})
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled != "function") { // 如果傳入的不是一個函式,預設建立空函式
onFulfilled = () => this.value; // 支援穿透
}
if (typeof onRejected != "function") { // 如果傳入的不是一個函式,預設建立空函式
onRejected = () => this.value; // 支援穿透
}
return new MyPromise((resolve, reject) => { // 返回一個新的Promise
if (this.status == MyPromise.FUFILLED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try {
let result = onFulfilled(this.value);
resolve(result);
} catch (e) {
reject(e); // 傳遞給下一個then
}
})
}
if (this.status == MyPromise.REJECTED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try {
let result = onRejected(this.value);
resolve(result);
} catch (e) {
reject(e); // 傳遞給下一個then
}
})
}
if (this.status == MyPromise.PENDING) { // 如果當前Promise是等待處理狀態,則將處理成功的函式與處理失敗的函式壓入非同步陣列。
this.callbacks.push({
onFulfilled: value => {
try {
let result = onFulfilled(value);
resolve(result);
} catch (e) {
reject(e); // 傳遞給下一個then
}
},
onRejected: value => {
try {
let result = onRejected(value);
resolve(result);
} catch (e) {
reject(e); // 傳遞給下一個then
}
}
});
}
});
}
}
then返回Promise
原生的Promise
支援返回一個新的Promise
,但是我們的Promise
現在還不支援。
其實也很簡單,判斷一下then()
中兩個函式返回的是不是一個新的Promise
,如果是的話則使用其then()
方法將其中resolve()
或reject()
的值進行傳遞。
class MyPromise {
static PENDING = "pending";
static FUFILLED = "fulfilled";
static REJECTED = "rejected";
constructor(executor) {
this.status = MyPromise.PENDING; // 初始狀態為準備狀態
this.value = null; // 初始值
this.callbacks = []; // 如果是一個非同步操作,則放入該陣列中
try {
executor(this.resolve.bind(this), this.reject.bind(this)); // 傳遞形參,執行executor函式
} catch (e) {
this.status = MyPromise.REJECTED; // 異常發生改變狀態
this.reject(e); // 記錄異常資訊
}
}
resolve(value) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.FUFILLED;
this.value = value;
this.callbacks.map(callback => { // // 呼叫處理非同步executor裡resolve的方法。
callback.onFulfilled(value);
})
}
}
reject(reason) {
if (this.status == MyPromise.PENDING) { // 限制
this.status = MyPromise.REJECTED;
this.value = reason;
this.callbacks.map(callback => { // 呼叫處理非同步executor裡reject的方法。
callback.onRejected(reason);
})
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled != "function") { // 如果傳入的不是一個函式,預設建立空函式
onFulfilled = () => this.value; // 支援穿透
}
if (typeof onRejected != "function") { // 如果傳入的不是一個函式,預設建立空函式
onRejected = () => this.value; // 支援穿透
}
return new MyPromise((resolve, reject) => { // 返回一個新的Promise
if (this.status == MyPromise.FUFILLED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try {
let result = onFulfilled(this.value);
if (result instanceof MyPromise) { // 判斷是否返回Promise物件
result.then(resolve, reject);
} else {
resolve(result); // 改變狀態並將值交由下一個then接收
}
} catch (e) {
reject(e); // 傳遞給下一個then
}
})
}
if (this.status == MyPromise.REJECTED) { // 狀態改變時執行
setTimeout(() => { // 晚於執行緒同步任務執行
try {
let result = onRejected(this.value);
if (result instanceof MyPromise) { // 判斷是否返回Promise物件
result.then(resolve, reject);
} else {
resolve(result); // 改變狀態並將值交由下一個then接收
}
} catch (e) {
reject(e); // 傳遞給下一個then
}
})
}
if (this.status == MyPromise.PENDING) { // 如果當前Promise是等待處理狀態,則將處理成功的函式與處理失敗的函式壓入非同步陣列。
this.callbacks.push({
onFulfilled: value => {
try {
let result = onFulfilled(value);
if (result instanceof MyPromise) { // 判斷是否返回Promise物件
result.then(resolve, reject);
} else {
resolve(result); // 改變狀態並將值交由下一個then接收
}
} catch (e) {
reject(e); // 傳遞給下一個then
}
},
onRejected: value => {
try {
let result = onRejected(value);
if (result instanceof MyPromise) { // 判斷是否返回Promise物件
result.then(resolve, reject);