Можно ли поместить await в тело defer внутри async-функции?
Нет. Тело defer остаётся синхронным контекстом, поэтому вызов, который может приостановить выполнение, нельзя размещать внутри него. defer может выполниться при выходе из функции после await, но сам не способен выполнить дополнительную асинхронную очистку.
defer предназначен для гарантированного синхронного освобождения ресурсов при выходе из области видимости: при обычном завершении, раннем return или распространении ошибки. Появление async/await добавило точки приостановки, но не изменило базовую семантику defer: отложенное действие должно быть выполнено как часть синхронного выхода из области.
Это ограничение предотвращает неоднозначность жизненного цикла функции: выход из неё не может превратиться в новую асинхронную операцию, которая ещё должна приостанавливаться и завершаться после формального выхода.
Предположим, функция получила ресурс, выполнила асинхронную работу и должна закрыть ресурс. Если закрытие само является async, естественно попытаться вызвать его через await в defer. Swift запрещает такой вариант.
Без правильной организации можно либо забыть очистить ресурс при ошибке, либо вызвать асинхронное закрытие только на успешном пути. Особенно опасно помещать синхронную блокировку или длительную очистку в defer: она выполнится гарантированно, но будет блокировать текущий исполнитель.
defer регистрирует синхронное действие, которое выполняется при выходе из текущей области. Наличие await в окружающей async-функции не делает тело defer асинхронным.
В примере defer выполнится после возобновления функции и её выхода, но его тело обязано завершиться синхронно. Добавление await перед асинхронным закрытием приведёт к ошибке компиляции.
Если очистка действительно асинхронная, её обычно выполняют явно в корректной точке жизненного цикла ресурса. При этом нужно отдельно решить, что должно произойти при ошибке или отмене: применить do-catch, использовать специальный асинхронный API управления ресурсом или изменить дизайн ресурса так, чтобы финальная операция была синхронной и короткой.
Главный компромисс такой: синхронный defer хорошо гарантирует факт выполнения очистки, но не подходит для операции, требующей приостановки. Явный await хорошо моделирует асинхронное завершение, но требует аккуратно покрыть все пути выхода.
defer выполняется при выходе именно из своей области, поэтому вложенная область может использоваться для ограничения времени жизни ресурса. Однако это не превращает отложенное действие в отдельную задачу и не гарантирует выполнение после возврата вызывающему коду.
Сервис открывает асинхронную транзакцию, затем выполняет сетевой запрос. Транзакцию нужно завершить асинхронным методом даже при ошибке запроса.
Первый вариант — вызвать асинхронное завершение в defer. Он не компилируется, потому что defer не поддерживает приостановку.
Второй вариант — вызвать завершение только после успешного запроса. Он проще, но оставляет транзакцию открытой при выброшенной ошибке или отмене задачи.
Выбранное решение — использовать API транзакции, в котором финализация синхронна и только планирует безопасное завершение, либо применить специальный асинхронный scope-объект, предоставляющий явный метод завершения и обработку ошибок. Если такого API нет, асинхронное завершение вызывают явно в do-catch, дублируя его в обработке ошибки и проверяя, что операция не запускается повторно.
Результат: ограничение defer учитывается в дизайне API, а не обходится созданием неуправляемой фоновой задачи. Запуск отдельной Task из defer обычно плох: вызывающий код может уже продолжить работу, ошибка станет независимой от исходной операции, а завершение ресурса потеряет строгую последовательность.
Да. await не завершает область видимости и не запускает defer заранее. Отложенное действие выполнится только при фактическом выходе из области — после нормального возврата, ошибки или отмены, если отмена привела к выходу через соответствующий путь.
Технически можно запустить отдельную задачу, но это не эквивалентно асинхронному defer. Такая задача становится отдельной незавершённой операцией: исходная функция не ждёт её окончания, ошибка не обязана попасть к вызывающему коду, а ресурс может быть использован после начала очистки. Этот подход допустим только при явно выбранной семантике фонового закрытия и безопасном времени жизни ресурса.
Нет. Сам defer гарантирует лишь запуск своего синхронного тела при выходе из области. Он не может дождаться асинхронной операции. Если вызывающему коду важно получить уже закрытый ресурс, очистку нужно выполнить через явный await до возврата или использовать абстракцию, которая сама управляет асинхронным временем жизни ресурса.