MiddleКодЧастоЕщё не отвечали
Оберните API с completion handler в async через checked continuation
Устаревший API загружает пользователя через completion handler. Оберните его в async throws-функцию с помощью checked continuation.
Требования:
- Верните
Userпри успехе, бросьте ошибку при сбое. - Возобновите continuation ровно один раз на каждом пути.
func legacyLoadUser(id: Int, completion: @escaping (Result<User, Error>) -> Void) { /* дано */ }
func loadUser(id: Int) async throws -> User {
// ваш код здесь
}
Допишите реализацию.
withCheckedThrowingContinuation даёт continuation; зовите resume один раз — с результатом или ошибкой. Повторный resume приводит к аварии, ведь на втором вызове он падает; без resume задача виснет навсегда.
- ✗Думают, что continuation можно возобновлять больше одного раза
- ✗Считают, что забытый
resumeуберётся сам, а не подвесит задачу - ✗Путают checked (падает) с unsafe (неопределённое поведение)
- →Что checked-вариант
withCheckedContinuationпроверяет во время выполнения? - →Как защитить continuation, если колбэк может сработать дважды?
resume(with:) разворачивает Result в нужный resume(returning:) / resume(throwing:), а колбэк на Result срабатывает один раз — значит, continuation возобновляется ровно один раз:
func loadUser(id: Int) async throws -> User {
try await withCheckedThrowingContinuation { continuation in
legacyLoadUser(id: id) { result in
continuation.resume(with: result) // ровно один раз: успех или ошибка
}
}
}
Если бы API мог позвать колбэк дважды, второй resume привёл бы к аварийному завершению; если бы мог не позвать вовсе — await завис бы навсегда. Поэтому обёрнутый API обязан гарантировать ровно один вызов.