1. Continuation
Мы уже говорили с вами про continuation. Здесь мы рассмотрим его применение немного в другом контексте. Предположим у нас есть метод, который отдаёт нам замыкание, а мы хотим сделать async функцию. Как быть в такой ситуации?
func loadData(by id: Int, completion: @escaping (Data) -> Void) { ... }
func fetchVideo(by id: Int) async -> Video {
await withCheckedContinuation { continuation in
loadData(by: id) { data in
continuation.resume(returning: data)
}
}
}
Типы Continuation¶
1) Checked Continuation (Checked Throwing Continuation) -- проверяют, что resume вызывается ровно один раз, предотвращая потенциальные ошибки выполнения
2) Unsafe Continuation (Unsafe Throwing Continuation) -- не включают такую проверку, но могут быть немного эффективнее. В данном случае эта ответственность лежит на разработчике, если вызвать resume повторно, то будет либо неопределённое поведение, либо креш.
RESUME в continuation ДОЛЖЕН БЫТЬ ВЫЗВАН РОВНО ОДИН РАЗ.
Отмена задач c continuation¶
-
Шаг 1: Проверка отмены задачи Перед тем, как возобновить выполнение с помощью continuation, проверьте, была ли задача отменена, используя Task.isCancelled. Если задача была отменена, вы можете завершить выполнение continuation без выполнения дальнейшей работы.
-
Шаг 2: Отмена выполняемой операции Если ваша асинхронная операция поддерживает отмену (например, сетевой запрос с использованием URLSessionTask), вы должны явно отменить эту операцию, когда обнаружите, что связанная с ней задача была отменена.
Пример:
func fetchData(url: URL) async -> Data? {
await withCheckedContinuation { continuation in
let task = URLSession.shared.dataTask(with: url) { data, response, error in
// Проверяем, была ли задача отменена
guard !Task.isCancelled else {
continuation.resume(throwing: CancellationError())
return
}
// Возвращаем данные или ошибку
if let data = data {
continuation.resume(returning: data)
} else if let error = error {
continuation.resume(throwing: error)
} else {
continuation.resume(throwing: URLError(.badServerResponse))
}
}
// Запускаем сетевой запрос
task.resume()
// Обработка отмены задачи
continuation.onTermination = { _ in
// Отменяем сетевой запрос, если задача была отменена
task.cancel()
}
}
}
// Использование
let url = URL(string: "https://example.com")!
let task = Task {
if let data = await fetchData(url: url) {
ружены: \(data)")
} else {
print("Ошибка загрузки данных или задача была отменена")
}
}
// Отмена задачи
task.cancel()