Асинхронная реактивность через ресурсы¶
30.09.2026
Все API сигналов синхронны: signal, computed, input и остальные. Приложению часто нужны данные, которые появляются асинхронно. Resource встраивает такие данные в код на сигналах и оставляет синхронный доступ к результату.
Resource подходит для любой асинхронной операции, но чаще всего им запрашивают данные с сервера. В примере ниже ресурс загружает данные пользователя.
Проще всего создать Resource функцией resource.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
Функция resource принимает объект ResourceOptions с двумя главными свойствами: params и loader.
Свойство params задаёт реактивное вычисление параметра. Когда меняется сигнал, прочитанный в этом вычислении, ресурс производит новое значение параметра, как это делает computed.
Свойство loader задаёт ResourceLoader — асинхронную функцию, которая получает состояние. Ресурс вызывает её каждый раз, когда вычисление params даёт новое значение, и передаёт это значение в загрузчик. Подробнее — ниже, в разделе Загрузчики ресурсов.
У Resource есть сигнал value: в нём результат загрузчика.
Загрузчики ресурсов¶
При создании ресурса указывают ResourceLoader. Это асинхронная функция: она принимает один аргумент, объект ResourceLoaderParams, и возвращает значение.
У объекта ResourceLoaderParams три свойства: params, previous и abortSignal.
| Свойство | Описание |
|---|---|
params | Значение вычисления params у ресурса. |
previous | Объект со свойством status, в котором лежит предыдущий ResourceStatus. |
abortSignal | AbortSignal. Подробности — в разделе Отмена запросов ниже. |
Если вычисление params вернуло undefined, загрузчик не запускается, а статус ресурса становится 'idle'.
Потоковые ресурсы¶
Некоторые асинхронные источники со временем отдают несколько значений, а не один результат. Например, WebSocket, Server-Sent Events (SSE) и слушатели onSnapshot в Firestore.
Для таких источников, которые обновляются непрерывно, берите stream. В отличие от loader, который для каждого запроса завершается один раз, stream возвращает сигнал, и его значение может обновляться и дальше, когда приходят новые данные.
loader — для разовой асинхронной операции, например запроса к HTTP-эндпоинту.
1 2 3 4 5 6 7 8 | |
Отмена запросов¶
Если вычисление params меняется, пока ресурс загружается, незавершённая загрузка отменяется.
На отмену отвечают через abortSignal из ResourceLoaderParams. Например, встроенный fetch принимает AbortSignal:
1 2 3 4 5 6 7 8 9 10 | |
Подробнее об отмене запроса через AbortSignal — в статье AbortSignal на MDN.
Перезагрузка¶
Загрузчик ресурса можно запустить из кода методом reload.
1 2 3 4 5 6 7 8 9 10 | |
Статус ресурса¶
У объекта ресурса есть сигнальные свойства: по ним читают состояние асинхронного загрузчика.
| Свойство | Описание |
|---|---|
value | Последнее значение ресурса или undefined, если значение ещё не получено. |
hasValue | Есть ли у ресурса значение. |
error | Последняя ошибка загрузчика ресурса или undefined, если ошибки не было. |
isLoading | Выполняется ли загрузчик ресурса сейчас. |
status | Конкретный ResourceStatus ресурса, как описано ниже. |
Сигнал status даёт конкретный ResourceStatus: состояние ресурса строковой константой.
| Статус | value() | Описание |
|---|---|---|
'idle' | undefined | У ресурса нет корректного запроса, загрузчик не запускался. |
'error' | undefined | Загрузчик столкнулся с ошибкой. |
'loading' | undefined | Загрузчик работает, потому что изменилось значение params. |
'reloading' | Предыдущее значение | Загрузчик работает, потому что вызван метод reload ресурса. |
'resolved' | Полученное значение | Загрузчик завершился. |
'local' | Значение, заданное локально | Значение ресурса задано локально через .set() или .update(). |
По статусу в интерфейсе условно показывают, например, индикатор загрузки и текст ошибки.
Кэширование данных resource при серверном рендеринге¶
При отрисовке на сервере загрузчик ресурса выполняется один раз и формирует исходный HTML. При гидратации браузер обычно запускает тот же загрузчик ещё раз.
Чтобы переиспользовать серверный результат, задайте ресурсу id. Angular сохраняет полученное значение в TransferState на сервере и на клиенте по нему инициализирует ресурс в состоянии 'resolved'.
1 2 3 4 5 6 7 | |
Значение id должно быть уникальным внутри приложения и совпадать на сервере и клиенте, чтобы Angular нашёл кэш того ресурса, который его запросил.
Кэш сериализуется в HTML страницы. Не ставьте id ресурсам с данными того пользователя, который запустил серверный рендеринг, особенно если этот HTML кэшируют или отдают нескольким пользователям.
Цепочка ресурсов¶
Иногда один ресурс зависит от результата другого. Эту зависимость описывают функцией chain из объекта контекста params.
1 2 3 4 5 6 7 8 9 10 11 | |
Здесь companyResource зависит от companyId пользователя, а он известен только после загрузки userResource. Вызов chain(userResource) читает значение userResource и сам переносит его статус на companyResource:
- Если
userResourceв состоянии idle,companyResourceтоже становитсяidle. - Если
userResourceзагружается или перезагружается (loading или reloading),companyResourceвходит в состояниеloading, и его загрузчик не запускается. Пока идётreloading,chainне возвращает ранее полученное значение. - Если
userResourceв состоянии ошибки (error),companyResourceтоже переходит вerror. - Если
userResourceполучен или задан локально (resolved или local),chainвозвращает текущее значение, иcompanyResourceберёт его как своиparams.
Когда chain переносит статус userResource (idle, loading, reloading или error), функция params на этом останавливается. Когда userResource в состоянии resolved или local, chain возвращает его значение, и это значение само может быть undefined. Пример учитывает это через chain(userResource)?.companyId: значение undefined даёт params, равные undefined, и companyResource становится idle.
Передавайте значение из цепочки напрямую как значение params, а не оборачивайте его в объект. Значение params вроде {companyId: undefined} всё равно считается заданным: загрузчик запустится с companyId, равным undefined, и ресурс не перейдёт в idle.
Цепочка и прямое чтение значения¶
Может показаться удобным прочитать значение ресурса прямо в params:
1 2 3 4 5 6 7 | |
Это работает, но undefined из params переводит ресурс в idle и не показывает настоящий статус ресурса выше по цепочке. chain лучше: он честно повторяет состояния loading и error.
Берите chain только тогда, когда зависимый ресурс сам делает асинхронную работу и ей нужно значение верхнего ресурса. Если значение нужно лишь синхронно вывести из ресурса, используйте computed.
Реактивная загрузка данных через httpResource¶
httpResource — обёртка над HttpClient: статус запроса и ответ доступны как сигналы. Запросы идут через HTTP-стек Angular, в том числе через перехватчики.
Композиция ресурсов через снимки¶
ResourceSnapshot — структурированный снимок текущего состояния ресурса. У каждого ресурса есть свойство snapshot, сигнал с этим состоянием.
1 2 3 4 5 6 7 8 | |
В снимке есть status и либо value, либо error.
Сборка ресурсов из снимков¶
Новые ресурсы собирают из снимков функцией resourceFromSnapshots. Так поведение ресурса меняют через сигнальные API, например computed и linkedSignal.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | |
Источник: https://angular.dev/guide/signals/resource