Search code examples
swiftmacosgrand-central-dispatch

Is code within DispatchQueue.global.async executed serially?


I have some code that looks like this:

DispatchQueue.global(qos: .userInitiated).async {
    self.fetchProjects()
    DispatchQueue.main.async {
          self.constructMenu()
    }
}

My question is are the blocks within the global block executed serially? When I add print statements, they always executed in the same order, but I'm not sure if I'm getting lucky as looking at the documentation, it says:

Tasks submitted to the returned queue are scheduled concurrently with respect to one another.

I wonder if anyone can shed any light on this?

EDIT:

Apologies, I don't think I made the question clear. I would like for the method constructMenu to only be called once fetchProjects has completed. From what I can tell (by logging print statements) this is the case.

But I'm not really sure why that's the case if what Apple's documentation above says (where each task is scheduled concurrently) is true.

Is code within an async block always executed serially, or is the fact that the code seems to execute serially a result of using DispatchQueue.main or is it just 'luck' and at some point constructMenu will actually return before fetchProjects?


Solution

  • I would like for the method constructMenu to only be called once fetchProjects has completed. From what I can tell (by logging print statements) this is the case.

    Yes, this is the case.

    But I'm not really sure why that's the case if what Apple's documentation above says (where each task is scheduled concurrently) is true.

    Apple’s documentation is saying that two separate dispatches may run concurrently with respect to each other.

    Consider:

    DispatchQueue.global(qos: .userInitiated).async {
        foo()
    }
    
    DispatchQueue.global(qos: .userInitiated).async {
        bar()
    }
    

    In this case, foo and bar may end up running at the same time. This is what Apple means by “Tasks submitted to the returned queue are scheduled concurrently.”

    But consider:

    DispatchQueue.global(qos: .userInitiated).async {
        foo()
        bar()
    }
    

    In this case, bar will not run until we return from foo.

    Is code within an async block always executed serially, or is the fact that the code seems to execute serially a result of using DispatchQueue.main or is it just ‘luck’ and at some point constructMenu will actually return before fetchProjects?

    No luck involved. It will never reach the DispatchQueue.main.async line until you return from fetchProjects.

    There is one fairly major caveat, though. This assumes that fetchProjects won’t return until the fetch is done. That means that fetchProjects better not be initiating any asynchronous processes of its own (i.e. no network requests). If it does, you probably want to supply it with a completion handler, and put the call to constructMenu in that completion handler.