Wiem, że jest to stare pytanie, ale natknąłem się na wątek i wydaje się, że istnieje tutaj związek między błędami a odrzuceniem, który działa w wielu przypadkach (przynajmniej w wielu przypadkach) często powtarzanej porady, aby nie używać obsługi wyjątków do zajmować się przewidywanymi przypadkami. Przykład: jeśli metoda asynchroniczna próbuje uwierzytelnić użytkownika, a uwierzytelnienie nie powiedzie się, jest to odrzucenie (jeden z dwóch przewidywanych przypadków), a nie błąd (np. Jeśli interfejs API uwierzytelniania był niedostępny).
Aby upewnić się, że nie tylko dzielę włosy, przeprowadziłem test wydajności trzech różnych podejść, używając tego kodu:
const iterations = 100000;
function getSwitch() {
return Math.round(Math.random()) === 1;
}
function doSomething(value) {
return 'something done to ' + value.toString();
}
let processWithThrow = function () {
if (getSwitch()) {
throw new Error('foo');
}
};
let processWithReturn = function () {
if (getSwitch()) {
return new Error('bar');
} else {
return {}
}
};
let processWithCustomObject = function () {
if (getSwitch()) {
return {type: 'rejection', message: 'quux'};
} else {
return {type: 'usable response', value: 'fnord'};
}
};
function testTryCatch(limit) {
for (let i = 0; i < limit; i++) {
try {
processWithThrow();
} catch (e) {
const dummyValue = doSomething(e);
}
}
}
function testReturnError(limit) {
for (let i = 0; i < limit; i++) {
const returnValue = processWithReturn();
if (returnValue instanceof Error) {
const dummyValue = doSomething(returnValue);
}
}
}
function testCustomObject(limit) {
for (let i = 0; i < limit; i++) {
const returnValue = processWithCustomObject();
if (returnValue.type === 'rejection') {
const dummyValue = doSomething(returnValue);
}
}
}
let start, end;
start = new Date();
testTryCatch(iterations);
end = new Date();
const interval_1 = end - start;
start = new Date();
testReturnError(iterations);
end = new Date();
const interval_2 = end - start;
start = new Date();
testCustomObject(iterations);
end = new Date();
const interval_3 = end - start;
console.log(`with try/catch: ${interval_1}ms; with returned Error: ${interval_2}ms; with custom object: ${interval_3}ms`);
Niektóre rzeczy, które tam są, są zawarte z powodu mojej niepewności co do interpretera Javascript (lubię schodzić tylko do jednej dziury królika na raz); na przykład, zawarłem doSomething
funkcję i przypisałem jej zwrot, aby dummyValue
mieć pewność, że bloki warunkowe nie zostaną zoptymalizowane.
Moje wyniki to:
with try/catch: 507ms; with returned Error: 260ms; with custom object: 5ms
Wiem, że istnieje wiele przypadków, w których polowanie na małe optymalizacje nie jest warte kłopotu, ale w systemach na większą skalę te rzeczy mogą mieć dużą kumulatywną różnicę, i jest to dość surowe porównanie.
SO… chociaż myślę, że podejście przyjęte w odpowiedzi jest słuszne w przypadkach, w których oczekujesz, że będziesz musiał poradzić sobie z nieprzewidywalnymi błędami w funkcji asynchronicznej, w przypadkach, gdy odrzucenie oznacza po prostu „musisz przejść do planu B (lub C lub D…) „Myślę, że preferuję odrzucenie przy użyciu niestandardowego obiektu odpowiedzi.
Promise
konstruktora antipattern ! Nawet pierwszy fragment powinien zostać napisanyfoo(id: string): Promise<A> { return someAsyncPromise().then(()=>{ return 200; }, ()=>{ throw 400; }); }