Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Synchronize nested async task

I have nested async tasks. The function below following this flow: loadEpisodes(load a list of episodes) -> use a array from completion to loop through each episode one and load comments ( one more async task ) for specific episode.

The problem is: comletion(fullyEpisodes) excuted before finishing comments load task. I tried to use Dispatch Group (second code block) but it's not working.

func loadComments(comletion: @escaping ([Episode]) -> Void){
loadEpisodes(completion: {
    episodes in
    var fullyEpisodes = [Episode]()
    for episode in episodes {
        WebService().load(resource: episode.comment, comletion: {
            comments in
            if let comments = comments {
                let _episode = Episode(id: episode.id, title: episode.title, comments: comments)
                fullyEpisodes.append(_episode)
                print("done")
            }
        })
    }
    comletion(fullyEpisodes)

})
}

Implemented Dispatch Group:

func loadComments(comletion: @escaping ([Episode]) -> Void){
    loadEpisodes(completion: {
        episodes in
        var fullyEpisodes = [Episode]()
        let group = DispatchGroup()
        for episode in episodes {
            group.enter()
            WebService().load(resource: episode.comment, comletion: {
                comments in
                if let comments = comments {
                    let _episode = Episode(id: episode.id, title: episode.title, comments: comments)
                    fullyEpisodes.append(_episode)
                    print("done")
                }
            })
            group.leave()
        }
        group.wait()
        group.notify(queue: .main, execute: {
            comletion(fullyEpisodes)
        })

    })
}

When I try to replace the comment load request by "print("something")" (not a new async task), Dispatch group is working.

like image 751
The Bao Avatar asked Dec 02 '16 14:12

The Bao


1 Answers

In your second example, (a) move group.leave() into the load() completion handler closure; and (b) remove the group.wait() altogether.

func loadComments(comletion: @escaping ([Episode]) -> Void){
    loadEpisodes(completion: {
        episodes in
        var fullyEpisodes = [Episode]()
        let group = DispatchGroup()
        for episode in episodes {
            group.enter()
            WebService().load(resource: episode.comment, comletion: {
                comments in
                if let comments = comments {
                    let _episode = Episode(id: episode.id, title: episode.title, comments: comments)
                    fullyEpisodes.append(_episode)
                    print("done")
                }
                group.leave()
            })
            // group.leave()
        }
        //group.wait()
        group.notify(queue: .main, execute: {
            comletion(fullyEpisodes)
        })

    })
}

or, cleaning that up a bit with trailing closure syntax and fixing completion spelling:

func loadComments(completion: @escaping ([Episode]) -> Void) {
    loadEpisodes { episodes in
        var fullyEpisodes = [Episode]()
        let group = DispatchGroup()
        for episode in episodes {
            group.enter()
            WebService().load(resource: episode.comment) { comments in
                if let comments = comments {
                    let _episode = Episode(id: episode.id, title: episode.title, comments: comments)
                    fullyEpisodes.append(_episode)
                }
                group.leave()
            }
        }
        group.notify(queue: .main) {
            completion(fullyEpisodes)
        }
    }
}
like image 52
Rob Avatar answered Oct 18 '22 11:10

Rob