英文:
Wrap async task into DispatchWorkItem in Swift to make it cancellable?
问题
我有一个带有异步方法的类。所有方法都非常简单:
class SomeClass {
func someFunc(params: SomeParams, completion: ((SomeResult) -> ())?) {
... // 一些代码,在其中异步调用完成回调,不仅使用 DispatchQueue.async
}
...//其他方法
}
现在我需要添加取消这些任务的可能性,但 SomeClass
不负责此操作,所以此类代码应该放在 SomeClass
外部的另一个类中。他们建议使用 DispatchWorkItem
,但如何正确包装这样的异步调用呢?
注意:有关如何将 DispatchQueue.global().async
替换为 DispatchWorkItem
的问题和答案,但不是关于如何包装现有任务的问题。
英文:
I have a class with async methods. All the methods are the simplest:
class SomeClass {
func someFunc(params: SomeParams, completion: ((SomeResult) -> ())?) {
... //some code where completion is called asynchronously, not only with DispatchQueue.async
}
...//other methods
}
Now I need to add a possibility to cancel such these tasks but SomeClass
is not responsible for it so such code should be placed into another class outside SomeClass
. They recommend to use DispatchWorkItem
but how wrap such async calls into it correctly?
Note: there are questions and answers how to replace DispatchQueue.global().async
with DispatchWorkItem
, not about wrapping existing tasks
答案1
得分: 1
我知道你已经解决了你的问题,但是为了日后的读者并回答标题中的问题,即是否可以将异步任务包装成 Swift 中的 DispatchWorkItem
以使其可取消?答案是,通常情况下,仅仅将其包装在 DispatchWorkItem
中是不够的。正如文档所告诉我们(重点强调):
取消会导致未来尝试执行工作项立即返回。取消不会影响已经开始执行的工作项。
如果你取消了已经启动的 DispatchWorkItem
,它只会设置该 DispatchWorkItem
的 isCancelled
属性,你可以在某些计算密集型例程中检查这个属性。但对于许多异步任务(比如网络请求),这将具有有限的实用性。
更糟糕的是,DispatchWorkItem
不适合包装一个本身就是异步的任务。DispatchWorkItem
通常会在异步任务被启动后立即完成工作项(除非你使用信号量之类的笨拙而脆弱的方法,这样做最好被视为反模式)。
你真的需要在 someFunc
内部包含取消逻辑。从历史上看,我们经常使用基于 GCD 框架的 Operation
子类,因为我们可以覆盖 cancel
以触发异步任务的取消。或者现在我们可能会使用Swift 并发的自动取消传播功能的 async
-await
。或者,你似乎已经在Combine中实现了这一点。
所以,归根结底,这取决于 someFunc
做了什么,但是 DispatchWorkItem
通常具有有限的实用性,尽管它具有(非常基本的)取消支持。
英文:
I know you’ve solved your problem already, but for the sake of future readers and in answer to the titular question whether you can “Wrap async task into DispatchWorkItem
in Swift to make it cancellable?”, the answer is that, no, simply wrapping it inside a DispatchWorkItem
is generally not sufficient. As the documentation tells us (emphasis added):
> Cancellation causes future attempts to execute the work item to return immediately. Cancellation does not affect the execution of a work item that has already begun.
If you cancel
a DispatchWorkItem
that you have already started, it merely sets isCancelled
property of that DispatchWorkItem
that you could check within some computationally intensive routine. But for many asynchronous tasks (like network requests), that would be of limited utility.
Worse, DispatchWorkItem
is not well suited to wrap a task that is, itself, asynchronous. The DispatchWorkItem
will generally finish the work item as soon as the asynchronous task has been launched, not waiting for it to complete (unless you do something kludgy and brittle, like using semaphores; so much so that it would best be considered an anti-pattern).
You really need to incorporate the cancelation logic within someFunc
. Historically, we often reached for Operation
subclasses (build upon the GCD framework) because we can then override cancel
to trigger the cancelation of the asynchronous task. Or nowadays we might reach for the automatic cancelation propagation offered by Swift concurrency’s async
-await
. Or, you appear to have achieved this with Combine.
So, bottom line, it depends upon what someFunc
was doing, but DispatchWorkItem
is often of limited utility, despite its (very basic) cancelation support.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论