Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift execute asynchronous tasks in order

I have a few asynchronous, network tasks that I need to perform on my app. Let's say I have 3 resources that I need to fetch from a server, call them A, B, and C. Let's say I have to finish fetching resource A first before fetching either B or C. Sometimes, I'd want to fetch B first, other times C first.

Right now, I just have a long-chained closure like so:

func fetchA() {
  AFNetworking.get(completionHandler: {
    self.fetchB()
    self.fetchC()
  })
}

This works for now, but the obvious limitation is I've hard-coded the order of execution into the completion handler of fetchA. Now, say I want to only fetchC after fetchB has finished in that completion handler, I'd have to go change my implementation for fetchB...

Essentially, I'd like to know if there's some magic way to do something like:

let orderedAsync = [fetchA, fetchB, fetchC]
orderedAsync.executeInOrder()

where fetchA, fetchB, and fetchC are all async functions, but fetchB won't execute until fetchA has finished and so on. Thanks!

like image 610
7ball Avatar asked Aug 14 '17 20:08

7ball


Video Answer


1 Answers

You can use a serial DispatchQueue mixed with a DispatchGroup which will ensure that only one execution block will run at a time.

let serialQueue = DispatchQueue(label: "serialQueue")
let group = DispatchGroup()
group.enter()
serialQueue.async{  //call this whenever you need to add a new work item to your queue
    fetchA{
        //in the completion handler call
        group.leave()
    }
}
serialQueue.async{
    group.wait()
    group.enter()
    fetchB{
        //in the completion handler call
        group.leave()
    }
}
serialQueue.async{
    group.wait()
    group.enter()
    fetchC{
        group.leave()
    }
}

Or if you are allowed to use a 3rd party library, use PromiseKit, it makes handling and especially chaining async methods way easier than anything GCD provides. See the official GitHub page for more info. You can wrap an async method with a completion handler in a Promise and chain them together like this:

Promise.wrap(fetchA(completion:$0)).then{ valueA->Promise<typeOfValueB> in
    return Promise.wrap(fetchB(completion:$0)
}.then{ valueB in

}.catch{ error in
    //handle error
}

Also, all errors are propagated through your promises.

like image 194
Dávid Pásztor Avatar answered Sep 29 '22 19:09

Dávid Pásztor