Wrap async task into DispatchWorkItem in Swift to make it cancellable?

342 views Asked by At

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

There are 1 answers

2
Rob On BEST ANSWER

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.