Якщо Promise відхилено, а обробника помилки (catch, другого аргументу then, try навколо await) немає, помилка стає необробленою.
У браузері сторінка продовжує працювати, але:
- у консолі з'являється
Uncaught (in promise) Error: ...; - на
windowспрацьовує подіяunhandledrejection.
У Node.js з версії 15 необроблене відхилення завершує процес з помилкою. Сервер на Node, де забули catch, падає від першого ж збою мережі.
Глобальні обробники в браузері:
// необроблені винятки в синхронному коді й колбеках
window.addEventListener('error', (event) => {
report({
message: event.message,
source: event.filename,
line: event.lineno,
stack: event.error?.stack,
});
});
// необроблені відхилення Promise
window.addEventListener('unhandledrejection', (event) => {
report({ message: String(event.reason), stack: event.reason?.stack });
// event.preventDefault() - прибрати повідомлення з консолі
});
event.reason - те, з чим відхилено Promise (зазвичай об'єкт Error, але може бути будь-що).
Подія rejectionhandled спрацьовує, якщо обробник додали пізніше, ніж браузер зафіксував відхилення як необроблене. Буває при «лінивому» підключенні catch.
Що ці обробники дають і чого ні:
- це останній рубіж для моніторингу, а не спосіб обробляти помилки. Зловити тут помилку й «продовжити» неможливо: функцію, що впала, уже не повернути;
- саме так працюють Sentry, Flare та інші системи: підписуються на ці події й надсилають звіти на сервер;
- помилки зі скриптів іншого домену (CDN) приходять як
Script error.без деталей - браузер приховує їх з міркувань безпеки. Деталі з'являться, якщо скрипт підключено зcrossorigin="anonymous"і сервер віддає заголовок CORS.
Типові джерела необроблених відхилень:
fetch(...).then(...)безcatchнаприкінці ланцюжка;- виклик async-функції без
await(«запустив і забув»):saveDraft();в обробнику події; Promise.all, у якому відхилився один Promise, - решта відхилень після першого вже ігноруються, але перше має бути оброблене.
Правило: кожен ланцюжок Promise має закінчуватися обробкою помилки, а «запущені й забуті» async-виклики - явним .catch(report). Лінтер (@typescript-eslint/no-floating-promises) знаходить такі місця автоматично.