В конструкции с несколькими async let дочерняя задача завершилась ошибкой до await: когда родительская задача узнает об этой ошибке?
Ошибка дочерней задачи не передаётся родителю в момент её возникновения. Родитель узнаёт о ней только при попытке получить результат через await; поскольку результат ошибочный, выражение должно быть try await. Если ошибка покидает область с async let, остальные незавершённые дочерние задачи отменяются, а область всё равно дожидается их завершения.
async let появился как часть структурированной конкурентности Swift. Подход решает проблему «осиротевших» задач: дочерняя работа получает ограниченный временем жизни родительский контекст, а её завершение связывается с областью видимости.
Это отличается от отдельно запущенной Task, чей результат можно вообще не запрашивать. Для async let компилятор и runtime поддерживают связь родителя с дочерней задачей, включая ожидание и отмену при выходе из области.
Две операции могут стартовать конкурентно, и одна из них способна завершиться ошибкой раньше, чем родитель дойдёт до получения результатов. Ошибка при этом не прерывает родителя автоматически: до точки await родительская задача может выполнять другой код.
Неверное предположение о немедленном распространении ошибки приводит к ошибочной логике очистки, преждевременному освобождению ресурсов или ожиданию, что соседняя операция уже отменена. Важно различать момент возникновения ошибки, момент её наблюдения и выход из области async let.
Каждый async let создаёт структурированную дочернюю задачу. Она может выполняться независимо и завершиться успешно, с ошибкой или отменой, но состояние ошибки хранится до тех пор, пока родитель не запросит результат.
Запрос результата имеет форму try await: await ожидает завершение, а try позволяет передать ошибку дальше или обработать её. До этого момента родительская задача не получает исключение автоматически и не обязана немедленно прекращать текущую работу.
Если ожидание одного результата приводит к выходу из области из-за ошибки, незавершённым async let отправляется отмена. Отмена в Swift кооперативная: дочерняя операция должна проверять состояние отмены или вызывать отменяемые API, поэтому сигнал отмены не гарантирует мгновенную остановку.
Перед фактическим выходом из области Swift дожидается завершения дочерних задач. Это сохраняет структурированность, но означает, что обработка ошибки может задержаться, если соседняя операция игнорирует отмену или выполняет длительную неотменяемую работу.
В примере first может завершиться ошибкой до того, как родитель начнёт ожидание. Ошибка становится видимой на try await (a, b), после чего незавершённая работа second получает отмену; её Task.checkCancellation() обнаруживает этот сигнал.
Экрану нужно одновременно загрузить профиль и рекомендации. Вариант с двумя независимыми Task позволяет быстро запустить операции, но требует вручную связывать их отмену с жизненным циклом экрана; иначе завершившаяся после ухода со страницы работа может обновить устаревшее состояние.
Вариант с последовательными вызовами проще, но увеличивает задержку, поскольку вторая загрузка начинается только после первой. Вариант с async let сохраняет параллельный запуск и связывает обе операции с областью родительской задачи.
Практическое решение — использовать async let, ожидать результаты в do-catch и сделать сетевые операции корректно отменяемыми. Если ошибка одной операции означает, что общий результат больше не нужен, выход из области отменит вторую; если операции действительно независимы, их лучше разделить на отдельные жизненные циклы и явно определить политику ошибок.
Дополнительный вопрос: Может ли родитель продолжить работу после того, как async let уже завершился ошибкой?
Ответ: Да. Ошибка хранится в дочерней задаче и не прерывает родителя до чтения результата. Родитель может выполнить синхронный код или ожидать другой результат, а ошибка будет выброшена только при соответствующем try await либо при неявном ожидании во время выхода из области.
Дополнительный вопрос: Что произойдёт, если родитель обработал ошибку одного async let, но остался внутри области?
Ответ: Само перехватывание ошибки не обязано отменять соседние async let. Если родитель продолжает находиться в области и позже получает другой результат, дочерняя задача может продолжить работу. Отмена остальных задач происходит, когда родительская область покидается из-за ошибки, отмены или другого управления потоком, а не просто из-за факта, что одна ошибка была поймана.
Дополнительный вопрос: Гарантирует ли ошибка одного async let, что родитель немедленно вернёт управление вызывающему коду?
Ответ: Нет. При выходе из области родитель сначала обеспечивает завершение дочерних задач. Им отправляется отмена, но отмена кооперативна; неотменяемая или игнорирующая отмену операция может задержать выход. Поэтому async let гарантирует структурированное завершение дочерней работы, но не мгновенный возврат после обнаружения ошибки.