Я знаю, что это старый вопрос, но я просто наткнулся на поток, и здесь, похоже, существует связь между ошибками и отклонением, что противоречит (во многих случаях, по крайней мере) часто повторяемого совета не использовать обработку исключений для иметь дело с ожидаемыми случаями. Чтобы проиллюстрировать: если асинхронный метод пытается аутентифицировать пользователя и аутентификация не удалась, это отклонение (один из двух ожидаемых случаев), а не ошибка (например, если API аутентификации был недоступен).
Чтобы убедиться, что я не просто раскалывал волосы, я провел тест производительности трех разных подходов к этому, используя этот код:
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`);
Некоторые вещи, которые там есть, включены из-за моей неуверенности в отношении интерпретатора Javascript (мне нравится проходить только одну кроличью нору за раз); например, я включил doSomething
функцию и назначил ее возврат, dummyValue
чтобы гарантировать, что условные блоки не будут оптимизированы.
Мои результаты были:
with try/catch: 507ms; with returned Error: 260ms; with custom object: 5ms
Я знаю, что есть много случаев, когда не стоит искать небольшие оптимизации, но в более крупных системах эти вещи могут иметь большое совокупное значение, и это довольно резкое сравнение.
ТАК ... хотя я думаю, что подход принятого ответа является обоснованным в тех случаях, когда вы ожидаете обработки непредсказуемых ошибок в рамках асинхронной функции, в тех случаях, когда отклонение просто означает «вам придется пойти с планом B (или C или D…) «Я думаю, что я предпочел бы отказаться от использования пользовательского объекта ответа.
Promise
конструктора антипаттерна ! Даже первый фрагмент должен был быть написанfoo(id: string): Promise<A> { return someAsyncPromise().then(()=>{ return 200; }, ()=>{ throw 400; }); }