Overuse of DispatchQueue.main.async?

I have code that I inherited - and there is very liberal use of DispatchQueue.main.async because they were concerned the UI code there wasn't on the main thread - but it already was on the main queue.

The bug I saw, was that if the calling code was already on the main thread, and the function they called also called the DispatchQueue.main.async, it seems like that call was delayed/not-called, and being requeued on the run loop for the next call.

Here's kind of a general - very stripped down - version of what I was experiencing:


    var result = 1

    override func viewDidLoad() {

        super.viewDidLoad()

        DispatchQueue.main.async {
            print("Before : result = \(self.result)")
            self.doStuff()
            print("After  : result = \(self.result)")
        }
    }

    func doStuff() {
        self.result += 1
        self.doStuff2()
    }

    func doStuff2() {
        DispatchQueue.main.async {
            self.result += 1
            self.doStuff3()
        }
    }

    func doStuff3() {
        result += 1
    }
}

The output is: Before : result = 1 After  : result = 2

So it enters doStuff2() but that function also calls DispatchQueue.main.async - and therefore, that code never gets executed.

I cleaned it up by removing these extraneous calls - but it took A LONG TIME tracking all of them down.

Is there any better way to debug this?

Also - they said this code worked in iOS 12.

Thanks, Scott

Forgot - this is iOS 15.5, Xcode 13.4.1

if the calling code was already on the main thread, and the function they called also called the DispatchQueue.main.async, it seems like that call was delayed/not-called, and being requeued on the run loop for the next call.

That is correct behavior, not a bug. No matter what queue you are currently executing on, the async() call just adds your closure to the target queue and returns.

Is there any better way to debug this?

No magic bullet here, but I’ve found this function is handy for verifying assumptions about what queue you are executing on:

dispatchPrecondition(condition: .onQueue(.main)) // or whatever queue you want to check

So it enters doStuff2() but that function also calls DispatchQueue.main.async - and therefore, that code never gets executed.

It does get executed.

But not until after the "print after" statement has executed.

Overuse of DispatchQueue.main.async?
 
 
Q