W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
promise 鏈在錯誤(error)處理中十分強大。當(dāng)一個 promise 被 reject 時,控制權(quán)將移交至最近的 rejection 處理程序。這在實際開發(fā)中非常方便。
例如,下面代碼中所 fetch
的 URL 是錯的(沒有這個網(wǎng)站),.catch
對這個 error 進行了處理:
fetch('https://no-such-server.blabla') // reject
.then(response => response.json())
.catch(err => alert(err)) // TypeError: Failed to fetch(這里的文字可能有所不同)
正如你所看到的,.catch
不必是立即的。它可能在一個或多個 .then
之后出現(xiàn)。
或者,可能該網(wǎng)站一切正常,但響應(yīng)不是有效的 JSON。捕獲所有 error 的最簡單的方法是,將 .catch
附加到鏈的末尾:
fetch('/article/promise-chaining/user.json')
.then(response => response.json())
.then(user => fetch(`https://api.github.com/users/${user.name}`))
.then(response => response.json())
.then(githubUser => new Promise((resolve, reject) => {
let img = document.createElement('img');
img.src = githubUser.avatar_url;
img.className = "promise-avatar-example";
document.body.append(img);
setTimeout(() => {
img.remove();
resolve(githubUser);
}, 3000);
}))
.catch(error => alert(error.message));
通常情況下,這樣的 .catch
根本不會被觸發(fā)。但是如果上述任意一個 promise rejected(網(wǎng)絡(luò)問題或者無效的 json 或其他),.catch
就會捕獲它。
promise 的執(zhí)行者(executor)和 promise 的處理程序周圍有一個“隱式的 try..catch
”。如果發(fā)生異常,它就會被捕獲,并被視為 rejection 進行處理。
例如,下面這段代碼:
new Promise((resolve, reject) => {
throw new Error("Whoops!");
}).catch(alert); // Error: Whoops!
……與下面這段代碼工作上完全相同:
new Promise((resolve, reject) => {
reject(new Error("Whoops!"));
}).catch(alert); // Error: Whoops!
在 executor 周圍的“隱式 try..catch
”自動捕獲了 error,并將其變?yōu)?rejected promise。
這不僅僅發(fā)生在 executor 函數(shù)中,同樣也發(fā)生在其處理程序中。如果我們在 .then
處理程序中 throw
,這意味著 promise rejected,因此控制權(quán)移交至最近的 error 處理程序。
這是一個例子:
new Promise((resolve, reject) => {
resolve("ok");
}).then((result) => {
throw new Error("Whoops!"); // reject 這個 promise
}).catch(alert); // Error: Whoops!
對于所有的 error 都會發(fā)生這種情況,而不僅僅是由 throw
語句導(dǎo)致的這些 error。例如,一個編程錯誤:
new Promise((resolve, reject) => {
resolve("ok");
}).then((result) => {
blabla(); // 沒有這個函數(shù)
}).catch(alert); // ReferenceError: blabla is not defined
最后的 .catch
不僅會捕獲顯式的 rejection,還會捕獲它上面的處理程序中意外出現(xiàn)的 error。
正如我們已經(jīng)注意到的,鏈尾端的 .catch
的表現(xiàn)有點像 try..catch
。我們可能有許多個 .then
處理程序,然后在尾端使用一個 .catch
處理上面的所有 error。
在常規(guī)的 try..catch
中,我們可以分析 error,如果我們無法處理它,可以將其再次拋出。對于 promise 來說,這也是可以的。
如果我們在 .catch
中 throw
,那么控制權(quán)就會被移交到下一個最近的 error 處理程序。如果我們處理該 error 并正常完成,那么它將繼續(xù)到最近的成功的 .then
處理程序。
在下面這個例子中,.catch
成功處理了 error:
// 執(zhí)行流:catch -> then
new Promise((resolve, reject) => {
throw new Error("Whoops!");
}).catch(function(error) {
alert("The error is handled, continue normally");
}).then(() => alert("Next successful handler runs"));
這里 .catch
塊正常完成。所以下一個成功的 .then
處理程序就會被調(diào)用。
在下面的例子中,我們可以看到 .catch
的另一種情況。(*)
行的處理程序捕獲了 error,但無法處理它(例如,它只知道如何處理 URIError
),所以它將其再次拋出:
// 執(zhí)行流:catch -> catch
new Promise((resolve, reject) => {
throw new Error("Whoops!");
}).catch(function(error) { // (*)
if (error instanceof URIError) {
// 處理它
} else {
alert("Can't handle such error");
throw error; // 再次拋出此 error 或另外一個 error,執(zhí)行將跳轉(zhuǎn)至下一個 catch
}
}).then(function() {
/* 不在這里運行 */
}).catch(error => { // (**)
alert(`The unknown error has occurred: ${error}`);
// 不會返回任何內(nèi)容 => 執(zhí)行正常進行
});
執(zhí)行從第一個 .catch
(*)
沿著鏈跳轉(zhuǎn)至下一個 (**)
。
當(dāng)一個 error 沒有被處理會發(fā)生什么?例如,我們忘了在鏈的尾端附加 .catch
,像這樣:
new Promise(function() {
noSuchFunction(); // 這里出現(xiàn) error(沒有這個函數(shù))
})
.then(() => {
// 一個或多個成功的 promise 處理程序
}); // 尾端沒有 .catch!
如果出現(xiàn) error,promise 的狀態(tài)將變?yōu)?“rejected”,然后執(zhí)行應(yīng)該跳轉(zhuǎn)至最近的 rejection 處理程序。但上面這個例子中并沒有這樣的處理程序。因此 error 會“卡住”。沒有代碼來處理它。
在實際開發(fā)中,就像代碼中常規(guī)的未處理的 error 一樣,這意味著某些東西出了問題。
當(dāng)發(fā)生一個常規(guī)的 error 并且未被 try..catch
捕獲時會發(fā)生什么?腳本死了,并在控制臺中留下了一個信息。對于在 promise 中未被處理的 rejection,也會發(fā)生類似的事。
JavaScript 引擎會跟蹤此類 rejection,在這種情況下會生成一個全局的 error。如果你運行上面這個代碼,你可以在控制臺中看到。
在瀏覽器中,我們可以使用 unhandledrejection
事件來捕獲這類 error:
window.addEventListener('unhandledrejection', function(event) {
// 這個事件對象有兩個特殊的屬性:
alert(event.promise); // [object Promise] —— 生成該全局 error 的 promise
alert(event.reason); // Error: Whoops! —— 未處理的 error 對象
});
new Promise(function() {
throw new Error("Whoops!");
}); // 沒有用來處理 error 的 catch
這個事件是 HTML 標準 的一部分。
如果出現(xiàn)了一個 error,并且在這沒有 .catch
,那么 unhandledrejection
處理程序就會被觸發(fā),并獲取具有 error 相關(guān)信息的 event
對象,所以我們就能做一些后續(xù)處理了。
通常此類 error 是無法恢復(fù)的,所以我們最好的解決方案是將問題告知用戶,并且可以將事件報告給服務(wù)器。
在 Node.js 等非瀏覽器環(huán)境中,有其他用于跟蹤未處理的 error 的方法。
.catch
? 處理 promise 中的各種 error:在 ?reject()
? 調(diào)用中的,或者在處理程序中拋出的 error。.then
? 的第二個參數(shù)(即 error 處理程序),那么 ?.then
? 也會以相同的方式捕獲 error。.catch
? 準確地放到我們想要處理 error,并知道如何處理這些 error 的地方。處理程序應(yīng)該分析 error(可以自定義 error 類來幫助分析)并再次拋出未知的 error(它們可能是編程錯誤)。.catch
? 也可以。unhandledrejection
? 事件處理程序(用于瀏覽器,以及其他環(huán)境的模擬),以跟蹤未處理的 error 并告知用戶(可能還有我們的服務(wù)器)有關(guān)信息,以使我們的應(yīng)用程序永遠不會“死掉”。你怎么看??.catch
? 會被觸發(fā)么?解釋你的答案。
new Promise(function(resolve, reject) {
setTimeout(() => {
throw new Error("Whoops!");
}, 1000);
}).catch(alert);
答案是:不,它不會被觸發(fā):
new Promise(function(resolve, reject) {
setTimeout(() => {
throw new Error("Whoops!");
}, 1000);
}).catch(alert);
正如本章所講,函數(shù)代碼周圍有個“隱式的 try..catch
”。所以,所有同步錯誤都會得到處理。
但是這里的錯誤并不是在 executor 運行時生成的,而是在稍后生成的。因此,promise 無法處理它。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: