我应该如何将带有回调队列的方法转换为异步/等待?

问题描述 投票:0回答:2

我想用 async/await 包装现有的基于 GCD 的函数,这些函数同时使用回调和回调队列。我想知道关于回调队列我应该遵循什么模式。即:

// This is what I have
func doWork(completeOn queue: DispatchQueue, completion: Result<Void, Error>) { ... }

// I want to wrap this in an async function
func doWork() async throws -> Void {
  try await withCheckedThrowingContinuation { continuation in
    doWork(completeOn: ???, completion: continuation.resume(with:))
  }
}

我不想偷懒并使用

DispatchQueue.main
完成队列(并且还会导致到主队列的无用跳转)。这里推荐什么?我无法重写 GCD 函数。

我确定这种模式足够频繁以存在解决方案,但在网上查找我找不到太多。

swift async-await grand-central-dispatch
2个回答
1
投票

坏消息 -

completeOn
与 Swift 的结构化并发不兼容。

Swift 不保证线程在

await
调用后继续执行。人们不应该做出假设,甚至不应该关心这一点。


好消息——你不需要它。

如果

completeOn
主要用于在主线程上传递,请使用
@MainActor
。如果使用
completeOn
来确保它在某个串行队列上交付,为了确保线程安全,请改用
actor


作为一般规则,避免混合使用 GCD 和结构化并发,尤其是当您处于结构化并发上下文中时。只需让 Swift 运行时进行线程管理即可。否则你可能会得到意想不到的结果。


对于您的特定情况,您可以传递一个全局队列,或者更改函数以使

queue
参数可选,并在传递
nil
时避免分派。


0
投票

您的第二个公式

func doWork() async throws
不可能重写您的原始公式,因为它丢失了指定队列的
completeOn
参数。如果你想要一个现代的公式,让你在没有完成处理程序的情况下做到这一点,请使用 Combine。

但更好的做法是:将整个功能扔到窗外,然后切换到

async/await
。你不需要回调,你不需要结果,你不需要指定队列,你根本不需要任何东西。只做工作!这就是重点。

// some code
let output = try await doAnythingYouLike()
// more code, use output as desired

这使它可以到达

doAnythingYouLike
它想要运行的地方。如果你想确保它不在主线程上运行,把它放在一个
actor
中。如果不是,请不要将其放入演员中并直接调用它,可能没有
try await
。但重点是,您的主要代码只是暂停、等待和连贯地恢复,而不用担心线程或队列。

实际上,我的意思是,在这个新世界中,您必须停止考虑线程和队列!你原来的方法是为一个过时的世界而建立的。扔掉它,学会以更新更快乐的方式思考。

请参阅我的 https://stackoverflow.com/a/75354516/341994 以了解确保计算脱离主线程的现代公式。

© www.soinside.com 2019 - 2024. All rights reserved.