Во время работы вашего Angular-приложения некоторый код может выбросить ошибку. Если оставить их необработанными, эти ошибки могут привести к неожиданному поведению и зависанию интерфейса. Это руководство описывает, как Angular справляется с ошибками, которые не были явно перехвачены кодом вашего приложения. Для получения рекомендаций по написанию собственной логики обработки ошибок обратитесь к лучшим практикам обработки ошибок в JavaScript и Angular.
Фундаментальный принцип стратегии обработки ошибок в Angular заключается в том, что ошибки должны быть видны разработчикам в месте вызова, когда это возможно. Этот подход гарантирует, что код, инициировавший операцию, имеет контекст, необходимый для понимания ошибки, ее правильной обработки и принятия решения о том, каким должно быть состояние приложения. Делая ошибки видимыми в месте их возникновения, разработчики могут реализовать обработку ошибок, специфичную для неудачной операции, и иметь доступ к информации, необходимой для восстановления или предоставления информативной обратной связи конечному пользователю. Это также помогает избежать "запашка" кода "Слишком общая ошибка" ( Overly general error), когда об ошибках сообщается без достаточного контекста для понимания их причины.
Например, рассмотрим компонент, который получает данные пользователя из API. Код, ответственный за выполнение вызова
API, должен включать обработку ошибок (например, используя блок try...catch или оператор catchError в RxJS) для
управления потенциальными проблемами сети или ошибками, возвращаемыми API. Это позволяет компоненту отобразить удобное
для пользователя сообщение об ошибке или повторить запрос, вместо того чтобы позволить ошибке распространиться без
обработки.
Необработанные ошибки передаются в ErrorHandler
Angular сообщает о необработанных ошибках в корневой ErrorHandler приложения. При предоставлении пользовательского
ErrorHandler, укажите его в ApplicationConfig при вызове bootstrapApplication.
При создании Angular-приложения вы часто пишете код, который вызывается автоматически самим фреймворком. Например,
Angular отвечает за вызов конструктора компонента и методов жизненного цикла, когда этот компонент появляется в шаблоне.
Когда фреймворк запускает ваш код, нет места, куда можно было бы разумно добавить блок try для корректной обработки
ошибок. В таких ситуациях Angular перехватывает ошибки и отправляет их в ErrorHandler.
Angular не перехватывает ошибки внутри API, которые вызываются непосредственно вашим кодом. Например, если у вас есть
сервис с методом, который выбрасывает ошибку, и вы вызываете этот метод в своем компоненте, Angular не перехватит эту
ошибку автоматически. Вы несете ответственность за ее обработку с использованием механизмов, таких как try...catch.
Angular перехватывает асинхронные ошибки из пользовательских промисов (promises) или Observable-потоков только тогда, когда:
- Существует явный контракт, по которому Angular должен ждать и использовать результат асинхронной операции, и
- Когда ошибки не представлены в возвращаемом значении или состоянии.
Например, AsyncPipe и PendingTasks.run пересылают ошибки в ErrorHandler, тогда как resource представляет ошибку
в свойствах status и error.
Ошибки, о которых Angular сообщает в ErrorHandler, являются неожиданными ошибками. Эти ошибки могут быть
неустранимыми или указывать на то, что состояние приложения повреждено. Приложения должны обеспечивать обработку ошибок
с использованием блоков try или соответствующих операторов обработки ошибок (таких как catchError в RxJS) в месте
возникновения ошибки, когда это возможно, вместо того чтобы полагаться на ErrorHandler. Последний чаще всего и
наиболее уместно используется только как механизм для сообщения о потенциально фатальных ошибках в инфраструктуру
отслеживания ошибок и логирования.
export class GlobalErrorHandler implements ErrorHandler {
private readonly analyticsService = inject(AnalyticsService);
private readonly router = inject(Router);
handleError(error: any) {
const url = this.router.url;
const errorMessage = error?.message ?? 'unknown';
this.analyticsService.trackEvent({
eventName: 'exception',
description: `Screen: ${url} | ${errorMessage}`,
});
console.error(GlobalErrorHandler.name, {error});
}
}
TestBed повторно выбрасывает ошибки по умолчанию
Во многих случаях ErrorHandler может только логировать ошибки и позволять приложению продолжать работу. Однако в
тестах вы почти всегда хотите, чтобы эти ошибки всплывали. TestBed в Angular повторно выбрасывает неожиданные ошибки,
чтобы гарантировать, что ошибки, перехваченные фреймворком, не будут случайно пропущены или проигнорированы. В редких
случаях тест может специально пытаться убедиться, что ошибки не приводят к зависанию или сбою приложения. В таких
ситуациях вы можете настроить TestBed так, чтобы он
не выбрасывал повторно ошибки приложения с помощью
TestBed.configureTestingModule({rethrowApplicationErrors: false}).
Глобальные слушатели ошибок
Ошибки, которые не перехвачены ни кодом приложения, ни экземпляром приложения фреймворка, могут достичь глобальной области видимости. Ошибки, достигающие глобальной области видимости, могут иметь непредвиденные последствия, если их не учитывать. В средах, отличных от браузера, они могут привести к сбою процесса. В браузере эти ошибки могут остаться незамеченными, и посетители сайта могут увидеть их в консоли браузера. Angular предоставляет глобальные слушатели для обеих сред, чтобы учесть эти проблемы.
Рендеринг на стороне клиента
Добавление provideBrowserGlobalErrorListeners()
в ApplicationConfig добавляет
слушатели 'error' и 'unhandledrejection' к окну браузера и пересылает эти ошибки в ErrorHandler. Angular CLI
генерирует новые приложения с этим провайдером по умолчанию. Команда Angular рекомендует обрабатывать эти глобальные
ошибки для большинства приложений либо с помощью встроенных слушателей фреймворка, либо с помощью собственных
пользовательских слушателей. Если вы предоставляете пользовательские слушатели, вы можете удалить
provideBrowserGlobalErrorListeners.
Рендеринг на стороне сервера и гибридный рендеринг
При использовании Angular с SSR, Angular автоматически добавляет слушатели 'unhandledRejection' и
'uncaughtException' к процессу сервера. Эти обработчики предотвращают сбой сервера и вместо этого логируют
перехваченные ошибки в консоль.
ВАЖНО: Если приложение использует Zone.js, добавляется только обработчик 'unhandledRejection'. Когда присутствует
Zone.js, ошибки внутри зоны приложения уже пересылаются в ErrorHandler приложения и не достигают процесса сервера.