Approximate example of code I need to achieve using GCD:
func performTask(completion: (() -> ())?) {
doSomeAsyncTask { [weak self] isSuccess in
if isSuccess {
self?.handleResult(completion: completion)
} else {
completion?()
self?.handleResult(completion: nil)
}
}
}
func handleResult(completion: (() -> ())?) {
…
}
func doSomeAsyncTask(completion: ((Bool) -> ())?) {
//example of async task which can be successful or not
DispatchQueue.main.async {
completion?(Bool.random())
}
}
I want to rewrite it with async/await but I don't know how to implement performTask. Other methods:
func handleResult() async {
…
}
func doSomeAsyncTask() async -> Bool {
await withCheckedContinuation { checkedContinuation in
Task {
checkedContinuation.resume(returning: Bool.random())
}
}
}
Could you please explain how to implement performTask? I can't understand how to deal with situation when sometimes method should call completion and sometimes not.
In your completion-handler rendition, if
doSomeAsyncTaskreturnsfalse, it is immediately calling the closure and then callinghandleResultwithnilfor the completion handler. This is a very curious pattern. The only thing I can guess is that the intent is to call the completion handler immediately ifdoSomeAsyncTaskfailed, but still callhandleResultregardless, but if successful, not to return untilhandleResultis done.If that is your intent, the Swift concurrency rendition might be:
Note, I am returning the success or failure of
doSomeAsyncTaskbecause, as a matter of best practice, you always want the caller to have the ability to know whether it succeeded or not (even if you do not currently care). So, I made it a@discardableResultin case the caller does not currently care whether it succeeded or failed.The other pattern would be to throw an error if unsuccessful (and the caller can
try?if it does not care about the success or failure at this point).Having shown a literal translation of the code you provided, I might suggest a simpler pattern:
In the first two alternatives, above, I use unstructured concurrency to handle the scenario where
doSomeAsyncTaskreturnedfalse, and allow the function to return a result immediately, and then performhandleResultasynchronously later. This (like the completion handler rendition) begs the question of how to handle cancelation. And the question is whetherhandleResultis slow enough to justify that pattern, or whether it was a case of premature optimization. It seems exceedingly strange that one would want to awaithandleResultin the success path and not in the failure path. This third and final example simplifies this logic. We do not know enough about the rationale for the original completion-handler rendition to answer that question at this point.