Отмена Task сама по себе не прерывает async throws-функцию. Что должно произойти, чтобы она завершилась CancellationError?
Функция должна кооперативно проверить отмену: явно вызвать проверку вроде Task.checkCancellation() либо использовать отменяемую операцию, которая сама выбрасывает CancellationError. Простая установка флага отмены не останавливает выполнение и не заставляет произвольную функцию автоматически выбросить ошибку.
Отмена в модели конкурентности Swift спроектирована как кооперативная, а не принудительная. Такой подход позволяет функции самой определить безопасную точку остановки и не прерывать критическую секцию посреди изменения состояния или работы с ресурсом.
Это особенно важно для структурированной конкурентности: вызывающий код может запросить отмену дочерней задачи, но корректное завершение зависит от того, наблюдает ли операция состояние отмены.
После отмены задачи функция может продолжить вычисления, если она не выполняет проверок и не вызывает отменяемые API. Поэтому нельзя считать, что факт вызова отмены гарантирует немедленное освобождение ресурсов, прекращение сетевого запроса или выполнение catch.
Неверная реализация может продолжить дорогую работу, записать результат уже неактуальной операции или скрыть отмену общим обработчиком ошибок. При этом отмена — это не обязательно ошибка бизнес-логики: часто её нужно передать выше или обработать отдельно.
Проверка Task.checkCancellation() немедленно выбрасывает CancellationError, если текущая задача отменена. Свойство Task.isCancelled только возвращает состояние и само по себе не завершает функцию.
Многие длительные системные операции также являются cancellation-aware. Например, Task.sleep реагирует на отмену и выбрасывает CancellationError. Для собственного CPU-интенсивного цикла проверки нужно добавить явно:
Проверка перед началом предотвращает запуск уже ненужной работы, а проверка после точки приостановки обнаруживает отмену, произошедшую во время ожидания. Если между проверками есть длительный синхронный цикл, проверку следует выполнять внутри него с подходящей частотой.
CancellationError не является единственным возможным результатом отмены: функция может выбросить собственную ошибку или вернуть значение, если таков её контракт. Поэтому вызывающий код не должен полагаться только на тип ошибки без понимания API.
Компромисс состоит в выборе частоты проверок. Слишком редкие проверки ухудшают отзывчивость, а слишком частые могут добавить лишние накладные расходы. Нельзя безопасно рассчитывать на принудительное прерывание произвольного синхронного кода.
Сервис загружает большой набор данных и после отмены экрана должен прекратить обработку. Вариант с единственной проверкой в начале не подходит: отмена может произойти во время сетевого ожидания или последующей обработки. Вариант с проверкой только isCancelled требует вручную выбрать способ выхода и легко приводит к тому, что функция продолжает выполнение после обнаружения флага.
Оптимальное решение — использовать отменяемые async-операции и добавить Task.checkCancellation() перед дорогостоящими этапами и между крупными порциями обработки. CancellationError следует отличать от настоящих сбоев: обычно его передают вызывающему уровню без показа пользователю сообщения об ошибке. В результате работа прекращается в предсказуемых точках, а ресурсы освобождаются обычным механизмом выхода, включая defer.
1. Гарантирует ли вызов отмены немедленное завершение задачи?
Нет. Отмена устанавливает состояние задачи и распространяется по предусмотренным правилам конкурентности, но не прерывает произвольный синхронный код. Завершение произойдёт только после проверки состояния или вызова API, реагирующего на отмену.
2. Означает ли любой CancellationError, что задача была отменена?
Нет. Такой экземпляр ошибки можно создать и выбросить вручную, даже если текущая задача не отменена. Для корректной диагностики нужно учитывать контракт конкретной операции и при необходимости проверять Task.isCancelled или использовать Task.checkCancellation().
3. Что произойдёт, если общий catch перехватит CancellationError?
Он обработает её как обычную ошибку, если не предусмотрено отдельное ветвление. Это может привести к ошибочному показу пользователю сообщения о сбое. Обычно отмену перехватывают отдельно для тихого завершения либо повторно выбрасывают, а остальные ошибки обрабатывают как реальные сбои.