Swift 5.5并发:如何让withCheckedThrowingContinuation在主线程恢复?
解决方案
一、MainActor完全适用这个场景
你希望fetch方法的调用者能直接在主线程拿到结果,MainActor就是专门处理这类UI相关并发上下文问题的,有两种实现方式:
1. 给整个方法标记@MainActor
直接给fetch方法加上@MainActor属性,方法的整个异步流程结束后会自动切换回主线程返回结果:
@MainActor static func fetch<T: GraphQLQuery>( query: T, cachePolicy: Apollo.CachePolicy = .default ) async throws -> T.Data? { try await withCheckedThrowingContinuation { continuation in Task { let network = try await Network.shared network.fetch( query: query, cachePolicy: cachePolicy ) { result in switch result { case .success: do { continuation.resume(with: .success(try result.get().data)) } catch { continuation.resume(with: .failure(GraphQLResultError.default)) } case let .failure(error): continuation.resume(with: .failure(error)) } } } } }
注意:如果Network.shared的初始化是耗时操作,这种方式会将其执行也放到主线程,可能阻塞UI。若Network.shared是轻量初始化或已提前准备好则无问题;否则建议把获取Network.shared的逻辑移出MainActor上下文。
2. 仅在恢复continuation时切换到MainActor
如果不想让整个方法都在MainActor执行,只需把continuation.resume的操作包裹在Task { @MainActor in ... }里:
static func fetch<T: GraphQLQuery>( query: T, cachePolicy: Apollo.CachePolicy = .default ) async throws -> T.Data? { try await withCheckedThrowingContinuation { continuation in Task { let network = try await Network.shared network.fetch( query: query, cachePolicy: cachePolicy ) { result in Task { @MainActor in switch result { case .success: do { continuation.resume(with: .success(try result.get().data)) } catch { continuation.resume(with: .failure(GraphQLResultError.default)) } case let .failure(error): continuation.resume(with: .failure(error)) } } } } } }
这种方式只让最终的resume操作在主线程执行,前面的网络请求初始化等逻辑仍在后台线程,不会影响主线程性能。
二、不使用MainActor的实现方式
如果不想依赖MainActor,直接用DispatchQueue.main.async包裹continuation.resume的调用,强制恢复操作在主线程执行:
static func fetch<T: GraphQLQuery>( query: T, cachePolicy: Apollo.CachePolicy = .default ) async throws -> T.Data? { try await withCheckedThrowingContinuation { continuation in Task { let network = try await Network.shared network.fetch( query: query, cachePolicy: cachePolicy ) { result in DispatchQueue.main.async { switch result { case .success: do { continuation.resume(with: .success(try result.get().data)) } catch { continuation.resume(with: .failure(GraphQLResultError.default)) } case let .failure(error): continuation.resume(with: .failure(error)) } } } } } }
这样continuation会在主线程恢复,调用者拿到结果时自然处于主线程,无需再手动切换。
额外优化:去掉不必要的Task嵌套
你代码里的Task嵌套可以简化,withCheckedThrowingContinuation本身是同步执行的,直接提前获取Network.shared即可,避免多余的任务创建:
@MainActor static func fetch<T: GraphQLQuery>( query: T, cachePolicy: Apollo.CachePolicy = .default ) async throws -> T.Data? { let network = try await Network.shared return try await withCheckedThrowingContinuation { continuation in network.fetch( query: query, cachePolicy: cachePolicy ) { result in switch result { case .success: do { continuation.resume(with: .success(try result.get().data)) } catch { continuation.resume(with: .failure(GraphQLResultError.default)) } case let .failure(error): continuation.resume(with: .failure(error)) } } } }
这样代码更简洁,也减少了不必要的线程切换。
内容的提问来源于stack exchange,提问作者Kunal Shah
相关产品推荐
相关产品推荐

