Пошук уроків, статей та іншого контенту
Синхронні помилки, error-first колбеки, необроблені відхилення Promise — і чому не можна їх ігнорувати.
Помилки в Node.js-застосунку виникають у трьох принципово різних формах, кожна з якою потребує свого способу обробки: синхронні винятки (try/catch), помилки в error-first колбеках (перевірка параметра error), і відхилені (rejected) Promise (catch чи try/catch навколо await).
// Синхронна помилка
try {
JSON.parse("не валідний json");
} catch (error) {
console.error("Синхронна помилка:", error.message);
}
// Error-first callback
fs.readFile("./missing.txt", (error, data) => {
if (error) {
console.error("Помилка колбека:", error.message);
return;
}
});
// Відхилений Promise
try {
await fetch("https://неіснуючий-домен.example");
} catch (error) {
console.error("Помилка Promise:", error.message);
}Якщо Promise відхилено (rejected), а коду, що обробив би це відхилення (.catch() чи try/catch навколо await), немає — Node.js видає попередження UnhandledPromiseRejection, а в сучасних версіях за замовчуванням завершує процес аварійно. Це навмисна поведінка: необроблена помилка означає, що застосунок опинився в непередбаченому стані, і продовжувати роботу небезпечніше, ніж зупинитись.
Node.js дозволяє підписатись на geть необроблені синхронні винятки на рівні всього процесу — але це призначено як останній рубіж для логування перед контрольованим завершенням процесу, а не як звичний спосіб «проковтнути» помилки й продовжити роботу:
process.on("uncaughtException", (error) => {
console.error("Критична необроблена помилка:", error);
process.exit(1); // завершити процес контрольовано, а не продовжувати в невизначеному стані
});Продовжувати роботу процесу після uncaughtException без завершення — небезпечна практика: стан застосунку після необробленого винятку не гарантовано коректний (наприклад, з'єднання могло лишитись у напіввідкритому стані), і подальші запити можуть оброблятись некоректно чи непередбачувано.
Так само, як у браузерному JavaScript (курс JS, урок про обробку помилок), власні класи помилок, що розширюють Error, дають змогу розрізняти типи помилок і реагувати на них по-різному — особливо корисно у HTTP-обробниках, де різні типи помилок мають повертати різний статус-код:
class NotFoundError extends Error {
constructor(message) {
super(message);
this.name = "NotFoundError";
this.statusCode = 404;
}
}
// В обробнику запиту:
try {
const user = await findUser(id);
if (!user) throw new NotFoundError(`Користувача ${id} не знайдено`);
} catch (error) {
const statusCode = error.statusCode || 500;
res.writeHead(statusCode);
res.end(error.message);
}Ігнорувати параметр error в error-first колбеку — помилка мовчки проходить непоміченою, і код продовжує роботу з відсутніми чи некоректними даними.
Використовувати process.on('uncaughtException') як спосіб «проковтнути» помилки й продовжити роботу процесу, а не як логування перед контрольованим завершенням — залишає застосунок у непередбачуваному стані.
Не обробляти відхилення async-функції, викликаної без await (fire-and-forget) — необроблене відхилення Promise, навіть якщо результат формально не потрібен викликачу.
У Node.js помилки виникають у трьох формах — синхронні винятки, error-first колбеки, відхилені Promise — і кожна вимагає власного способу обробки; необроблене відхилення Promise за замовчуванням аварійно завершує процес у сучасних версіях Node.js. process.on('uncaughtException') — останній рубіж для логування перед контрольованим завершенням процесу, а не звичний спосіб обробки очікуваних помилок, для яких краще підходять try/catch і кастомні класи помилок.