Я прочитал несколько статей, в которых предлагался блок try / catch для каждого запроса. Есть ли правда в этом?
Нет, это не обязательно. try/catch
с await
работает концептуально, как try/catch
работает с обычными синхронными исключениями. Если вы просто хотите обрабатывать все ошибки в одном месте и хотите, чтобы весь ваш код просто прерывался одним обработчиком ошибок, независимо от того, где произошла ошибка, и вам не нужно перехватывать одну конкретную ошибку, чтобы вы могли сделать что-то особенное для этой конкретной ошибки, тогда один try/catch
- это все, что вам нужно.
Но если вам нужно конкретно обработать одну конкретную ошибку, возможно, даже разрешив продолжить работу остальной части кода, тогда вам может понадобиться более локальный обработчик ошибок, который может быть локальным try/catch
или .catch()
на локальная асинхронная операция, которая возвращает обещание.
или я должен смешать его с обещаниями, как во втором фрагменте кода.
Формулировка этого говорит о том, что вы можете не совсем понять, что происходит с await
, потому что обещания участвуют в обоих ваших кодовых блоках.
В обоих ваших кодовых блоках models.user.findById(decoded.userId);
возвращает обещание. У вас есть два способа использовать это обещание.
Вы можете использовать await
с ним, чтобы «приостановить» внутреннее выполнение функции, пока это обещание не разрешится или не отклонится.
Вы можете использовать .then()
или .catch()
, чтобы увидеть, когда обещание разрешается или отклоняется.
Оба используют обещание, возвращаемое из вашего models.user.findById(decoded.userId);
вызова функции. Таким образом, вашу фразу было бы лучше сказать "или если бы я использовал локальный обработчик .catch()
для конкретного обещания вместо того, чтобы перехватывать все отклонения в одном месте.
Делаем это:
// skip second async operation if there's an error in the first one
async function someFunc() {
try {
let a = await someFunc():
let b = await someFunc2(a);
return b + something;
} catch(e) {
return "";
}
}
Аналогично соединению вашего обещания с одним обработчиком .catch()
в конце:
// skip second async operation if there's an error in the first one
function someFunc() {
return someFunc().then(someFunc2).catch(e => "");
}
Независимо от того, какая асинхронная функция отклоняет, применяется один и тот же обработчик ошибок. Если первый отклоняет, второй не выполняется, так как поток переходит непосредственно к обработчику ошибок. Это прекрасно, если вы хотите, чтобы поток шел, когда в первой асинхронной операции произошла ошибка.
Но предположим, что вы хотели, чтобы ошибка в первой функции была превращена в значение по умолчанию, чтобы всегда выполнялась вторая асинхронная операция. Тогда этот поток контроля не сможет этого достичь. Вместо этого вам нужно будет зафиксировать первую ошибку прямо в источнике, чтобы вы могли указать значение по умолчанию и продолжить обработку со второй асинхронной операцией:
// always run second async operation, supply default value if error in the first
async function someFunc() {
let a;
try {
a = await someFunc():
} catch(e) {
a = myDefaultValue;
}
try {
let b = await someFunc2(a);
return b + something;
} catch(e) {
return "";
}
}
Аналогично соединению вашего обещания с одним обработчиком .catch()
в конце:
// always run second async operation, supply default value if error in the first
function someFunc() {
return someFunc()
.catch(err => myDefaultValue)
.then(someFunc2)
.catch(e => "");
}
Примечание: это пример, который никогда не отклоняет обещание, которое возвращает someFunc()
, а скорее предоставляет значение по умолчанию (пустая строка в этом примере), а не отклоняет, чтобы показать вам различные способы обработки ошибок в этой функции. Это, конечно, не требуется. Во многих случаях просто возвращение отклоненного обещания является правильным, и этот абонент может решить, что делать с ошибкой отклонения.