Navjeeven Mann
Navjeeven Mann

Reputation: 93

Why do we call completion on main thread in asynchronous function?

For example in this function,

func asyncAdd(_ input: (Int, Int),
runQueue: DispatchQueue = DispatchQueue.global(qos: .userInitiated),
completionQueue: DispatchQueue = DispatchQueue.main,
completion: @escaping (Result<Int, SlowAddError>) -> ()) {
runQueue.async {
    let result = input.0 + input.1
    completionQueue.async {
        completion(result)
    }
 }
}

Why is completionQueue, the main thread, have to be where the completion is called. Could we not just call the completion on a background thread?

Upvotes: 1

Views: 691

Answers (2)

CouchDeveloper
CouchDeveloper

Reputation: 19106

"Could we not just call the completion on a background thread?"

Well, in the general case I would vote for "no" if this background thread is a worker thread. You have to decide from case to case whether you can do this safely or not:

The object that asynchronously performs the task - lets call it "operation", may have special requirements on the worker threads (or dispatch queues) where the task is performed. This could include a special quality of service level (see DispatchQoS), the fact that it is a serial queue, etc. Worker threads are internal to the "Operation" for dedicated use to run their tasks, but possibly not suited to run other tasks or functions.

When the operation calls the completion handler on its worker thread, it has no more control of what happens with their worker threads. The client could for example directly perform extended CPU intensive calculations, thereby stalling the operation because its worker thread is now busy and has to wait until the client's completion handler completed. These calculation may also run on the QoS defined by the operation which is not suitable at all for the client's requirements.

So, that all could introduce performance issues into a complex asynchronous system or make it even unpredictable in its behaviour.

An example is URLSession, which does not call the completion handler on its internal worker thread, but on a dedicated "callback" OperationQueue. When using URLSession you either provide it, or URLSession will create one for you.

Upvotes: 0

Rob Napier
Rob Napier

Reputation: 299325

Yes, but it's often convenient to call completion handlers on the main queue, since it's common to want to update the UI, and calling UI updaters on the wrong queue is a common bug.

It is not required, nor even universally recommended, to make general-purpose async functions call back on the main queue. But for certain systems where the use cases are well-known, it's very convenient and helps prevent bugs.

The above example, of course, is not useful, and would be very unlikely to be found in production code. But that doesn't change the utility in specialized cases. Another common pattern is for the caller to pass a completion queue. That's also fine, particularly for very general-purpose tools (such as URLSession, which uses this approach).

Upvotes: 2

Related Questions