如何将带完成回调的后台请求逻辑转为Async/Await实现?
问题:将带后台回调缓存的请求逻辑转为Async/Await形式
我在NetworkManager类中有一个通过完成回调返回Bar对象的getBar方法。当应用处于后台时,我会把请求和回调存入FooManager,等应用回到前台时重新发起请求并完成原回调,逻辑如下:
// 原基于回调的实现 func getBar( withRequestId requestId: String, completion: (Bar) -> Void ) { let applicationState = UIApplication.shared.applicationState if applicationState == .background { let request = BarRequest( id: requestId, completion: completion ) fooManager.request = request return } // 前台时直接执行请求逻辑... }
当FooManager收到applicationDidBecomeActive通知时,重新调用请求并触发原回调:
func applicationDidBecomeActive() { guard let request else { return } network.getBar( withRequestId: request.id, completion: request.completion ) self.request = nil }
现在要把getBar转为async throws形式,但因为没有可直接保存的回调,不知道怎么实现等价逻辑:
// 待实现的Async/Await版本 func getBar(withRequestId requestId: String) async throws -> Bar { let applicationState = UIApplication.shared.applicationState if applicationState == .background { let configRequest = ConfigRequest( id: requestId, completion: completion // 此处无法直接获取回调 ) configManager.configRequest = configRequest return } // 前台时的异步请求逻辑... }
解决方案:利用Continuation保存异步延续
在Async/Await模式下,可以通过CheckedThrowingContinuation(对应throws场景)捕获异步函数的执行延续,把它和请求信息一起保存,等应用回到前台时再恢复执行。
步骤1:定义包含Continuation的请求结构体
修改保存请求的结构体,用CheckedThrowingContinuation替代原回调:
struct PendingBarRequest { let requestId: String let continuation: CheckedThrowingContinuation<Bar, Error> }
步骤2:修改Async版本的getBar方法
在getBar中,当应用处于后台时,通过withCheckedThrowingContinuation获取延续,将其存入FooManager并挂起函数;前台时直接执行异步请求逻辑:
func getBar(withRequestId requestId: String) async throws -> Bar { let applicationState = UIApplication.shared.applicationState if applicationState == .background { return try await withCheckedThrowingContinuation { continuation in // 创建包含延续的待处理请求 let pendingRequest = PendingBarRequest( requestId: requestId, continuation: continuation ) // 存入FooManager fooManager.pendingRequest = pendingRequest } } // 前台时直接执行异步请求逻辑(示例) return try await performBarRequest(with: requestId) } // 实际发起请求的异步方法(示例) private func performBarRequest(with requestId: String) async throws -> Bar { // 这里写真实的请求逻辑,比如网络请求、本地读取等 }
步骤3:修改FooManager的前台恢复逻辑
当应用回到前台时,取出保存的PendingBarRequest,调用异步版本的getBar,拿到结果后恢复延续,或捕获错误并传递:
class FooManager { var pendingRequest: PendingBarRequest? func applicationDidBecomeActive() { guard let pendingRequest = pendingRequest else { return } // 异步发起请求,避免阻塞主线程 Task { do { let bar = try await network.getBar(withRequestId: pendingRequest.requestId) // 成功时恢复延续,返回结果 pendingRequest.continuation.resume(returning: bar) } catch { // 失败时恢复延续,抛出错误 pendingRequest.continuation.resume(throwing: error) } // 清空待处理请求 self.pendingRequest = nil } } }
注意事项
- 必须确保
continuation被恰好调用一次,否则会导致运行时崩溃或内存泄漏。即使应用被终止,也要考虑在applicationWillTerminate中用合适的错误恢复未完成的延续。 - 如果需要支持多个待处理请求,可以把
pendingRequest改成数组,批量处理。 CheckedThrowingContinuation会做运行时检查,确保延续只被调用一次,开发阶段能快速发现问题;如果追求性能,也可以用UnsafeContinuation,但需要自己保证调用的安全性。
内容的提问来源于stack exchange,提问作者Tometoyou
相关产品推荐
相关产品推荐

