You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 21:47:45