简体   繁体   中英

Set order for background operations with completion handler in main thread

I have the following method that executes on a background thread every time the app starts:

func setup() {
   loadPlayers()
   loadTeams()
}

Each of these methods invokes a webService, which is also done in the background thread, and the parse the JSON received to load the data in memory. For some reason, these process of parsing the data is done in the main thread.

Teams are composed by players, so I must set all the info from the players before loading the teams. How can I make this? I have tried the following code but loadTeams() is still invoked before all the players are loaded.

func setup() {   
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
       self.loadPlayers()
    })
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
      self.loadTeams()
    })
}

From what it sounds like, your loadPlayers() and loadTeams() functions are happening asynchronously. Therefore it doesn't matter if you're using dispatch_sync , as the functions will return immediately, dispatch_sync will also return immediately.

As trick14 said , you will need to implement a callback into these functions yourself. I would imagine the 'webService' you're using would implement some kind of callback system anyway.

For example, you'd want to have your functions do something like this:

func loadPlayers(callback: () -> ()) {

    // I don't know what API you're using, but it must implement some form of callback system...

    doAsynchronousNetworkTaskWithCompletion({success in
        callback()
    })
}

Then you can simply dispatch these tasks onto your background queue, using the callbacks to queue the next tasks. For example:

func setup() {

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {

        self.loadPlayers({
            self.loadTeams({
                dispatch_async(dispatch_get_main_queue(), {
                    // do UI update
                })
            })
        })
    })

}

The only problem with this method is you'll start to create a pyramid of doom, which isn't great for readability.

To counter this, you can use dispatch_group to schedule a completion block to fire when a given number of tasks have finished. As your tasks are asynchronous, you'll have to use the functions dispatch_group_enter() and dispatch_group_leave() to manually increment and decrement the number of tasks running.

For example:

func setup() {

    let group = dispatch_group_create()

    dispatch_group_enter(group) // Increment the number of tasks in the group
    loadShirts({
        dispatch_group_leave(group) // Decrement the number of tasks in the group
        print("finished shirts")
    })

    dispatch_group_enter(group)
    loadStadiums({
        dispatch_group_leave(group)
        print("finished stadiums")
    })

    dispatch_group_enter(group)
    loadPlayers({
        dispatch_group_leave(group)
        print("finished players")
    })

    // gets fired when the number of tasks in the group reaches zero.
    dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { 
        self.loadTeams({
            dispatch_async(dispatch_get_main_queue(), {
                // do UI update
                print("finished")
            })
        })
    })
}

It's also worth noting that using a dispatch_sync onto a background queue from the main queue doesn't guarantee that the tasks will be run on a background thread.

Because you're blocking the main thread when you do this, GCD will often optimise by simply running that code on the main thread anyway (as transferring to another thread is expensive). See here for more info.

Therefore you want to be using dispatch_async wherever you can and synchronising within the background operation itself , although this doesn't matter in your case as your tasks are asynchronous anyway.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM