При вызове async throws-функции как изменяется поток выполнения, если она выбрасывает ошибку после await?
Если async throws-функция выбрасывает ошибку, выполнение вызывающего кода немедленно переходит к подходящему catch или ошибка передаётся дальше вызывающей функции. Оператор await только ожидает завершения асинхронной операции, а try обозначает возможный выброс; сам по себе await ошибку не обрабатывает. Код после try await в текущей ветке не выполняется.
Модель throws отделяет обычный результат функции от аварийного завершения без использования специальных значений вроде nil или кодов ошибок. Это позволяет передавать ошибку вверх по стеку вызовов и обрабатывать её на уровне, где уже известен подходящий контекст.
С появлением асинхронных функций тот же механизм был сохранён: async отвечает за приостановку и возобновление работы, а throws — за канал ошибок. Разделение этих обязанностей делает возможной комбинацию асинхронности и обычной структурированной обработки ошибок.
Асинхронная операция может приостановить функцию на await, а после возобновления завершиться ошибкой. Ошибочно считать, что после await всегда будет получено значение или что ошибка автоматически превратится в результат типа Optional.
Если ошибка не перехвачена локально, она должна быть объявлена в сигнатуре текущей функции. Иначе компилятор не позволит вызвать throws-операцию: вызывающий код обязан явно выбрать стратегию — обработать ошибку или передать её дальше.
try await выполняется концептуально в два этапа: await допускает приостановку до завершения асинхронной операции, а try разрешает получить её успешное значение с возможным выбросом ошибки. После возобновления функция либо возвращает значение, либо выбрасывает ошибку; третьего успешного продолжения после этой точки нет.
Если вызов находится внутри do, управление переходит к первому подходящему catch. Если подходящего обработчика нет, ошибка покидает текущую функцию. Тогда эта функция тоже должна быть объявлена как throws; для асинхронной функции обычно используется сочетание async throws.
В примере await не поглощает ошибку. После выброса NetworkError.offline переменная text не получает значения, строка с её выводом пропускается, а управление переходит в соответствующий catch.
Перехватывать ошибку локально стоит, когда текущий слой может выполнить осмысленное действие: показать состояние интерфейса, повторить запрос или преобразовать техническую ошибку в доменную. Если такой информации у слоя нет, обычно лучше сохранить исходную ошибку и передать её выше, не скрывая причину сбоя.
Сервис загрузки профиля вызывается из экранной модели. При отсутствии сети экран должен показать специальное состояние, а неизвестные ошибки должен обработать общий механизм диагностики.
Рассматривались три варианта. Локальный catch в сетевом сервисе позволил бы скрыть детали реализации, но смешал бы транспортный слой с логикой интерфейса. Преобразование результата в Result сделало бы ошибку явным значением, однако потребовало бы отдельного соглашения для каждого вызывающего слоя и не использовало бы естественную цепочку throws.
Выбран вариант с async throws: сервис передаёт ошибку без потери, а экранная модель перехватывает только известную ей доменную причину. В результате сетевой код остаётся переиспользуемым, а пользовательское состояние формируется на подходящем уровне.
Можно ли вызвать async throws-функцию только с await, без try?
Нет, если вызов действительно может выбросить ошибку. await сообщает о возможной приостановке, но не заменяет try; компилятор потребует обработать или передать ошибку. Исключение составляют случаи, когда ошибка заранее преобразована другим механизмом, например вызов обёрнут в Result или используется try?.
Что произойдёт, если вызывающая функция объявлена только как async, но не как throws?
Прямой вызов async throws-функции будет отклонён компилятором, если ошибка не перехвачена внутри do-catch или не преобразована в другое значение. Добавление async разрешает ожидание, но не даёт права автоматически распространять ошибки.
Может ли ошибка быть выброшена во время приостановки, пока вызывающая задача не выполняется?
Да. Асинхронная функция может завершить операцию ошибкой до или после возобновления вызывающей задачи. С точки зрения вызывающего кода результатом возобновления будет либо значение, либо ошибка; обработка остаётся структурированной и выполняется в месте вызова, где записан try await.