為什么要使用 Promise?
在探討 Promise 之前,我們先來看一下為什么需要它的出現(xiàn)。
JavaScript 中有一個重要概念 - 異步 (async),它允許我們在執(zhí)行耗時任務(wù)時,不必等待程序完成,而是繼續(xù)執(zhí)行下面的代碼,直到任務(wù)完成再通知。常用的異步操作有:文件操作、數(shù)據(jù)庫操作、AJAX 以及定時器等。
JavaScript 有兩種實現(xiàn)異步的方式:
第一種:回調(diào)函數(shù) callback function
在 ES6 promise 出現(xiàn)之前,通常使用回調(diào)函數(shù) (callback) 實現(xiàn)異步操作。但使用回調(diào)函數(shù) callback 存在一個明顯的缺點,當(dāng)需要執(zhí)行多個異步操作時,代碼會不斷往內(nèi)嵌套,這種情況通常被稱為“回調(diào)地獄”(callback hell)。
callback(() => {
console.log("Hello!");
callback(() => {
console.log("Hello!");
callback(() => {
console.log("Hello!");
callback(() => {
console.log("Hello!");
}, 200);
}, 200);
}, 200);
}, 200);
而為了解決這種問題,就出現(xiàn)了第二種方法 - promise。
什么是 Promise?
上一段提到 Promise 出現(xiàn)的原因,這一段我們來看那到底 Promise 是什么。
Promise 照英文上的意思,是約定、承諾,它代表的意涵是這個約定請求會在未來每個時刻返回數(shù)據(jù)給調(diào)用者。在 MDN 文件中,Promise 是用來表示 一個異步操作的最終完成(或失敗)及其結(jié)果值。
怎么使用 Promise
Promise 是一個 構(gòu)造函數(shù),我們需要通過 new 關(guān)鍵字創(chuàng)建一個 Promise。而 Promise 會接收一個函數(shù)作為參數(shù),這個函數(shù)又稱為 executor,executor 會立即執(zhí)行。如下方代碼,若丟入瀏覽器開發(fā)者工具執(zhí)行,console 的結(jié)果會立刻被打打打印出來。
new Promise((resolve, reject) => {
console.log("executor 立即執(zhí)行"); // executor 立即執(zhí)行
});
而 executor 函數(shù),會再接受另外兩個函數(shù)參數(shù)
- resolve 實現(xiàn)函數(shù):如下方代碼,請求成功的例子,正確的時候會調(diào)用 resolve 函數(shù),并返回結(jié)果。
- reject 拒絕函數(shù):如下方代碼,請求失敗的例子,失敗的時候會調(diào)用 reject 函數(shù),并返回結(jié)果。
function requestData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (url === "explainthis.io") {
resolve("hello welcome to explainthis");
} else {
reject("it is not explainthis");
}
}, 3000);
});
}
// 1. 請求成功
requestData("explainthis.io").then((res) => {
console.log(res); //hello welcome to explainthis
});
//2. 請求失敗
requestData("explainthis.com").catch((e) => console.log(e)); //it is not explainthis
Promise 的狀態(tài)
一個 Promise 一定會處于以下三種狀態(tài)的其中一種
- pending:初始狀態(tài),執(zhí)行了 executor,但還在等待中。
- fulfilled:表示操作完成,執(zhí)行 resolve 函數(shù)。
- rejected:表示操作失敗,執(zhí)行 reject 函數(shù)。
then
的使用
延續(xù)前段談到的,異步用第一種 callback 做法很容易有 callback hell 的產(chǎn)生,而使用 Promise 的好處則可以避免這種難以閱讀的寫法。
Promise 可以用一種鏈?zhǔn)?(chaining) 的方式將這些異步操作串連,如下方代碼示例,我們可以通過 then 來將等完成之后的操作串起。
function requestData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (url === "explainthis.io") {
resolve("hello welcome to explainthis");
} else {
reject("it is not explainthis");
}
}, 3000);
});
}
requestData("explainthis.io")
.then((res) => {
console.log(res); //hello welcome to explainthis
return 1;
})
.then((res) => {
console.log(res); // 1
return 2; //hello welcome to explainthis
})
.then((res) => {
console.log(res); // 2
});
- then 方法可以接受兩個參數(shù),一個為成功的回調(diào),另一個為失敗的回調(diào)
function requestData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (url === "explainthis.io") {
resolve("hello welcome to explainthis");
} else {
reject("it is not explainthis");
}
}, 0);
});
}
requestData("explainthis.com").then(
(res) => {
console.log(res);
},
(reason) => {
console.log(reason);
}
);
//it is not explainthis
錯誤處理
Promise 的一個好處是錯誤處理,最簡單的方式是在加上一個 catch 來捕捉錯誤,并執(zhí)行一些錯誤處理代碼。如下方代碼,如果請求失敗,例如由于網(wǎng)絡(luò)故障,則 Promise 會被拒絕。在這種情況下,catch 方法將捕獲錯誤,并輸出錯誤訊息。
fetch("https://explainthis.com/data")
.then((response) => response.json())
.then((data) => {
console.log(data);
})
.catch((error) => {
console.error("oops!", error);
})
.finally(() => {
console.log("close loader");
});
finally
方法
如果有加上 finally
,那 Promise 狀態(tài)不論是 fulfilled 還是 rejected 都會需要執(zhí)行 finally
方法。finally
是 Promise 處理流程中一個非常有用的方法,它可以幫助我們在不管 Promise 是否成功的狀態(tài)下,執(zhí)行一定必要的操作。
使用場景例如,一進入頁面要先從服務(wù)器 fetch 數(shù)據(jù),等待的同時會顯示 loading 的畫面,而最后不論是否有拿到數(shù)據(jù),我們都需要把 loader 關(guān)閉。這時候,關(guān)閉 loader 的邏輯,就很適合放在 finally 中。如下方代碼:
fetch("https://explainthis.com/data")
.then((response) => response.json())
.then((data) => {
console.log(data);
})
.catch((error) => {
console.error(error);
})
.finally(() => {
console.log("close loader");
});
什么是 async/await?
async/await 是一種基于 Promise 之上的語法糖,比 Promise 的寫法更像是同步操作。
首先,我們會使用 async 關(guān)鍵字將函數(shù)標(biāo)記為異步函數(shù),異步函數(shù)就是指返回值為 Promise 對象的函數(shù)。
在異步函數(shù)中我們可以調(diào)用其他的異步函數(shù),不過不是使用 then(),而是使用 await 語法,await 會等待 Promise 完成之后直接返回最終的結(jié)果。
async function getData() {
const res = await fetch("https://getsomedata");
const data = await res.json();
console.log(data);
}
getData();
該文章在 2024/8/11 3:08:56 編輯過