只是想先發制人地說,我熟悉 JavaScript 中的 async/await 和 promises,因此無需為此將我鏈接到某些 MDN 頁面。
我有一個功能來獲取用戶詳細資訊并將其顯示在 UI 上。
async function someHttpCall() {
throw 'someHttpCall error'
}
async function fetchUserDetails() {
throw 'fetchUserDetails error'
}
function displayUserDetails(userDetails) {
console.log('userDetails:', userDetails)
}
async function fetchUser() {
try {
const user = await someHttpCall()
try {
const details = await fetchUserDetails(user)
returndisplayUserDetails(details)
} catch (fetchUserDetailsError) {
console.log('fetching user error', fetchUserDetailsError)
}
} catch (someHttpCallError) {
console.log('networking error:', someHttpCallError)
}
}
它首先通過 via 進行 HTTP 呼叫someHttpCall
,如果成功則繼續進行fetchUserDetails
,成功后我們將在 Ui via 上顯示詳細資訊returndisplayUserDetails
。
如果someHttpCall
失敗,我們將停止并且不fetchUserDetails
打電話。換句話說,我們希望將錯誤處理someHttpCall
和資料處理從fetchUserDetails
我寫的函式是帶有嵌套try catch
塊的,如果嵌套變深,它就不能很好地擴展,我試圖使用普通then
和catch
這是我的第一次嘗試
function fetchUser2() {
someHttpCall()
.then(
(user) => fetchUserDetails(user),
(someHttpCallError) => {
console.log('networking error:', someHttpCallError)
}
)
.then(
(details) => {
displayUserDetails(details)
}, //
(fetchUserDetailsError) => {
console.log('fetching user error', fetchUserDetailsError)
}
)
}
問題是第二個then
會運行,即displayUserDetails
即使someHttpCall
失敗。為了避免這種情況,我不得不讓前面的.catch
塊拋出
所以這是更新的版本
function fetchUser2() {
someHttpCall()
.then(
(user) => fetchUserDetails(user),
(someHttpCallError) => {
console.log('networking error:', someHttpCallError)
throw someHttpCallError
}
)
.then(
(details) => {
displayUserDetails(details)
}, //
(fetchUserDetailsError) => {
console.log('fetching user error', fetchUserDetailsError)
}
)
}
然而,現在第二個 catch 將作為 throw 的結果被呼叫。所以當someHttpCall
失敗時,在我們處理someHttpCallError
錯誤后,我們會進入這個(fetchUserDetailsError) => { console.log('fetching user error', fetchUserDetailsError) }
不好的塊,因為fetchUserDetails
從來沒有被呼叫所以我們不需要處理fetchUserDetailsError
(我知道在這種情況下someHttpCallError
變成fetchUserDetailsError
了)
我可以在那里添加一些條件檢查來區分這兩個錯誤,但這似乎不太理想。所以我想知道如何通過使用.then
并.catch
在這里實作相同的目標來改進這一點。
uj5u.com熱心網友回復:
我可以在那里添加一些條件檢查來區分這兩個錯誤,但這似乎不太理想。
實際上,這聽起來像是一個理想的情況。這意味著您不必嵌套任何try / catch
可以使您的代碼更具可讀性的塊。這是async / await
要解決的問題之一。
一個解決方案可能是通過擴展Error
介面來創建自定義錯誤,以便能夠確定錯誤發生的方式和位置。
class CustomError extends Error {
constructor(name, ...args) {
super(...args)
this.name = name
}
}
在與錯誤對應的函式中拋出您的錯誤。
async function someHttpCall() {
throw new CustomError('HttpCallError', 'someHttpCall error');
}
async function fetchUserDetails(user) {
throw new CustomError('UserDetailsError', 'fetchUserDetails error')
}
現在,您可以通過檢查錯誤的name
屬性來區分錯誤,從而控制錯誤流。
async function fetchUser() {
try {
const user = await someHttpCall()
const details = await fetchUserDetails(user)
return displayUserDetails(details)
} catch (error) {
switch(error.name) {
case 'HttpCallError':
console.log('Networking error:', error)
break
case 'UserDetailsError':
console.log('Fetching user error', error)
break
}
}
}
uj5u.com熱心網友回復:
我想知道如何通過使用
.then
并.catch
在這里實作相同的目標來改進這一點
如果您想復制相同的行為,則無法避免嵌套:
function fetchUser2() {
return someHttpCall().then(
(user) => {
return fetchUserDetails(user).then(
(details) => {
return displayUserDetails(details)
},
(fetchUserDetailsError) => {
console.log('fetching user error', fetchUserDetailsError)
}
)
},
(someHttpCallError) => {
console.log('networking error:', someHttpCallError)
throw someHttpCallError
}
)
}
(完全等同于try
/catch
將使用.then(…).catch(…)
代替.then(…, …)
,但您可能實際上并不想要那個。)
我寫的函式是 [nested],如果嵌套變深,它就不能很好地擴展,我試圖重寫它以獲得更好的可讀性 […]
為此,我建議結合await
使用.catch()
:
async function fetchUser() {
try {
const user = await someHttpCall().catch(someHttpCallError => {
throw new Error('networking error', {cause: someHttpCallError});
});
const details = await fetchUserDetails(user).catch(fetchUserDetailsError => {
throw new Error('fetching user error', {cause: fetchUserDetailsError});
});
return displayUserDetails(details);
} catch (someError) {
console.log(someError.message, someError.cause);
}
}
(cause
選項Error
仍然很新,你可能需要一個 polyfill)
uj5u.com熱心網友回復:
我一直在鼓舞銹病的Result
型別(您處理沿途每一個潛在的錯誤,力)。
所以我所做的是在每個單獨的函式中處理例外,決不允許拋出例外,而是回傳一個錯誤(如果出現問題)或所需的回傳值(如果沒有發生例外)。這是我如何做的一個例子(包括評論):
TS游樂場
如果您不熟悉 TypeScript,可以在上面的 TypeScript Playground 鏈接(頁面右側)中查看以下代碼的純 JavaScript 版本(沒有型別資訊)。
// This is the code in my exception-handling utility module:
// exception-utils.ts
export type Result <T = void, E extends Error = Error> = T | E;
export function getError (value: unknown): Error {
return value instanceof Error ? value : new Error(String(value));
}
export function isError <T>(value: T): value is T & Error {
return value instanceof Error;
}
export function assertNotError <T>(value: T): asserts value is Exclude<T, Error> {
if (value instanceof Error) throw value;
}
// This is how to use it:
// main.ts
import {assertNotError, getError, isError, type Result} from './exception-utils.ts';
/**
* Returns either Error or string ID,
* but won't throw because it catches exceptions internally
*/
declare function getStringFromAPI1 (): Promise<Result<string>>;
/**
* Requires ID from API1. Returns either Error or final number value,
* but won't throw because it catches exceptions internally
*/
declare function getNumberFromAPI2 (id: string): Promise<Result<number>>;
/**
* Create version of second function with no parameter required:
* Returns either Error or final number value,
* but won't throw because it catches exceptions internally
*
* The previous two functions work just like this, using the utilities
*/
async function fetchValueFromAPI2 (): Promise<Result<number>> {
try {
const id = await getStringFromAPI1(); // Error or string
assertNotError(id); // throws if `id` is an Error
return getNumberFromAPI2(id); // Error or number
}
catch (ex) {
return getError(ex);
}
}
async function doSomethingWithValueFromAPI2 (): Promise<void> {
const value = await fetchValueFromAPI2(); // value is number or Error
if (isError(value)) {
// handle error
}
else console.log(value); // value is number at this point
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/388358.html
標籤:javascript 异步 异步等待 es6-promise
上一篇:顫振異步等待未按預期作業
下一篇:返回列表