swift 带有`withCheckedThrowingContinuation`的帮助函数,将在主线程上恢复

wbgh16ku  于 2023-05-05  发布在  Swift
关注(0)|答案(1)|浏览(228)

我正在努力将Swift 5.5的并发特性引入我的应用程序,并从一个单一的方法开始。这是我目前掌握的情况:

效用方法

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))
                }
            }
        }
    }
}

在调试时,我发现continuation在任意方法上恢复,而不是在调用它们的线程上。从我上面的例子中,network.fetch的回调总是在主线程上,但是continuation.resume再次将执行转移到任意线程。

型号编码

static func fetchData() async throws -> [SomeData] {
    let query = SomeQuery()
    do {
        let data = try await Util.fetch(query: query)
        // Still on a background thread
        return data!
    }
}

查看编码

private func fetchData() async {
    do {
        let data = try await Model.fetchData()
        
        // This feels incorrect since this will be set after the `fetchData` function returns
        DispatchQueue.main.async {
            result = (Result.success(data), [:])
        }
    } catch {
        result = (Result.failure(error), [:])
    }
}

我想应该有一些方法可以在fetch(query:)方法中运行主线程的延续,这样当我在每个视图上设置属性时就不必切换到主线程。这是MainActor的用例之一吗?如果我不想使用MainActor,我该怎么做?

kyxcudwk

kyxcudwk1#

考虑一下你的“视图代码”,在这里你将代码分派到主队列:

private func fetchData() async {
    do {
        let data = try await Model.fetchData()
        
        // This feels incorrect since this will be set after the `fetchData` function returns
        DispatchQueue.main.async {
            result = (Result.success(data), [:])
        }
    } catch {
        result = (Result.failure(error), [:])
    }
}

在Swift并发中,我们不再使用旧的GCD“分派”模式。相反,如果我们想要在主线程上运行一个属性或方法,我们只需将其隔离到@MainActor

@MainActor
private func fetchData() async {
    do {
        let data = try await Model.fetchData()
        result = (Result.success(try await data, [:])
    } catch {
        result = (Result.failure(error), [:])
    }
}

或者可以将此方法放在与主参与者隔离的类型中。这将实现同样的事情。
FWIW,许多来自GCD的人被MainActor.run {…}吸引,这让人想起我们的老朋友DispatchQueue.main.async {…}。但是我们真的希望使用全局actor指定,@MainActor,用于必须在主线程上执行的事情,因为我们现在喜欢对并发代码进行编译时验证。
关于将遗留代码转换为async-await的实用技巧,我可能会建议WWDC 2021视频Swift concurrency: Update a sample app。它很好地实际演示了这一迁移过程。

相关问题