[英]Javascript Promise Pattern - differentiate error
考慮這個代碼塊:
getUser(userId)
.catch(function(error){
crashreporter.reportError('User DB failed', error);
// show user a generic error
})
.then(function(user) {
return chargeCreditCard(user);
})
.catch(function(error){
crashreporter.reportError('Credit card failed', error);
// show user an error saying that their credit card got rejected
})
顯然,這樣做的問題是如果用戶數據庫失敗,THEN(USER) 塊就會被執行。 另一種選擇是將第一個捕獲塊移動到鏈的末端。 然而,這會導致另一個問題! 我們將無法區分錯誤是來自用戶數據庫還是信用卡。
我認為可以解決問題的以下模式是否被視為 Promise Anti Pattern? 有沒有更好的方法來做到這一點? 我看到的問題是,您可能會陷入半回調地獄。
getUser(userId)
.then(function(user) {
return chargeCreditCard(user)
.catch(function(error){
crashreporter.reportError('Credit card failed', error);
// show user an error saying that their credit card got rejected
});
})
.catch(function(error){
crashreporter.reportError('User DB failed', error);
// show user a generic error
})
編輯:我想我還不是很清楚。 如果有更多 THEN 塊怎么辦,如下所示。 問題是,一旦您遇到一個 ERROR,您就根本不希望鏈條繼續下去。
getUser(userId)
.then(function(user) {
return chargeCreditCard(user);
}, function(error){
crashreporter.reportError('User DB failed', error);
// show user a error 1
})
.then(function(chargeId) {
return saveChargeId(chargeId);
}, function(error){
crashreporter.reportError('ChargeId DB failed', error);
// show user a error 2
})
.then(function(chargeHistoryId) {
return associateChargeToUsers(chargeHistoryId);
}, function(error){
crashreporter.reportError('chargeHistoryId DB failed', error);
// show user a error 3
})
.catch(function(error){
crashreporter.reportError('Credit card failed', error);
// show user a error 4
})
我認為可以解決問題的以下模式是否被視為 Promise Anti Pattern?
不,還好。
有沒有更好的方法來做到這一點?
是的,有看的區別.then(…).catch(…)
和.then(…, …)
如果你想嚴格區分成功案例(繼續)和錯誤案例(報告這個特定問題),向then
傳遞兩個回調是一個更好的主意。 這樣,外部處理程序不能由成功案例代碼的失敗觸發,只能由安裝它的承諾中的失敗觸發。 在你的情況下:
getUser(userId)
.then(function(user) {
return chargeCreditCard(user)
.then(function(chargeId) {
return saveChargeId(chargeId)
.then(function(chargeHistoryId) {
return associateChargeToUsers(chargeHistoryId);
.then(function(result) {
return finalFormatting(result);
}, function(error){
crashreporter.reportError('chargeHistoryId DB failed', error);
return 3;
});
}, function(error){
crashreporter.reportError('ChargeId DB failed', error);
return 2;
});
}, function(error){
crashreporter.reportError('Credit card failed', error);
return 4;
});
}, function(error){
crashreporter.reportError('User DB failed', error);
return 1;
})
.then(showToUser);
盡管您可能想使用通用錯誤處理程序:
getUser(userId)
.catch(function(error){
crashreporter.reportError('User DB failed', error);
throw new Error(1);
})
.then(function(user) {
return chargeCreditCard(user)
.catch(function(error){
crashreporter.reportError('Credit card failed', error);
throw new Error(4);
});
})
.then(function(chargeId) {
return saveChargeId(chargeId);
.catch(function(error){
crashreporter.reportError('ChargeId DB failed', error);
throw new Error(2);
});
})
.then(function(chargeHistoryId) {
return associateChargeToUsers(chargeHistoryId);
.catch(function(error){
crashreporter.reportError('chargeHistoryId DB failed', error);
throw new Error(3);
});
})
.then(function(result) {
return finalFormatting(result);
}, function(error) {
return error.message;
})
.then(showToUser);
在這里,每個then
回調確實返回一個承諾,該承諾本身會因適當的錯誤而拒絕。 理想情況下,每個被調用的函數都已經這樣做了,當它們沒有這樣做並且您需要為每個函數附加一個特定的catch
,您可能想要使用包裝器輔助函數(可能作為crashreporter
一部分?)。
function withCrashReporting(fn, msg, n) {
return function(x) {
return fn(x)
.catch(function(error){
crashreporter.reportError(msg, error);
throw new Error(n);
});
};
}
withCrashReporting(getUser, 'User DB failed', 1)(userId)
.then(withCrashReporting(chargeCreditCard, 'Credit card failed', 4))
.then(withCrashReporting(saveChargeId, 'ChargeId DB failed', 2))
.then(withCrashReporting(associateChargeToUsers, 'chargeHistoryId DB failed', 3))
.then(finalFormatting, function(error) {
return error.message;
})
.then(showToUser);
我看到的問題是,您可能會陷入半回調地獄。
不,這只是適當的包裝級別。 與回調地獄相反,它可以被壓縮到最多兩個嵌套,並且它總是有一個返回值。
如果您絕對想避免嵌套和回調,請使用async
/ await
,盡管這實際上更難看:
try {
var user = await getUser(userId);
} catch(error) {
crashreporter.reportError('User DB failed', error);
return showToUser(1);
}
try {
var chargeId = chargeCreditCard(user);
} catch(error) {
crashreporter.reportError('Credit card failed', error);
return showToUser(4);
}
try {
var chargeHistoryId = saveChargeId(chargeId);
} catch(error) {
crashreporter.reportError('ChargeId DB failed', error);
return showToUser(2);
}
try {
var result = associateChargeToUsers(chargeHistoryId);
} catch(error) {
crashreporter.reportError('chargeHistoryId DB failed', error);
return showToUser(3);
}
return showToUser(finalFormatting(result));
這是您無需嵌套回調即可完成“取消”鏈的方法。 這就是為什么承諾是“回調地獄”的解決方案......
var errors = {
USER: 'User DB failed',
CHARGE: 'ChargeId DB failed',
HISTORY: 'chargeHistoryId DB failed',
};
function onError(type, error) {
throw {
message: type,
error: error
}
}
getUser(userId)
.then(chargeCreditCard, onError.bind(null, errors.USER))
.then(saveChargeId, function (error) {
if (error.message === errors.USER) throw error
else onError(errors.CHARGE, error);
})
.then(associateChargeToUsers, function(error) {
if (error.message === errors.CHARGE || error.message === errors.USER) throw error
else onError(errors.HISTORY, error);
})
.then(finalFormatting, function(error) {
crashreporter.reportError(error.message, error.error);
})
.then(showToUser);
本質上,如果第一個 Promise 失敗,您會將其錯誤傳遞到失敗回調鏈中,並在到達末尾時將其記錄到日志中。 沒有創建其他承諾,因此您在第一次失敗時有效地“取消”了操作。
您的鏈應該只有一個catch
,但是您可以為每個函數中拋出的錯誤添加更多上下文。 例如,當chargeCreditCard
出現錯誤情況時,您可以將與您要報告的內容相對應的message
屬性chargeCreditCard
到錯誤上。 然后在您的catch
錯誤處理程序中,您可以將該message
屬性傳遞給報告者:
getUser(userId)
.then(chargeCreditCard)
.catch(reportError);
function reportError(error) {
crashreporter.reportError(error.message, error);
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.