Reputation: 1499
I'm looping through a table's rows, for each of them I'm doing a couple of async calls like fetching data from API, copying files, running shell script... How do I wait for the result until going to the next one.
Also I'm new to Swift, not sure if this is the best way to handle a group of async tasks. Should I use concurrency in this case ?
tableView.selectedRowIndexes.forEach { row in
myData.fetch(url: urlList[row]) { res in
self.anotherAsyncCall(res) { data in
//continue to deal with next row now
}
}
}
Upvotes: 1
Views: 153
Reputation: 438437
If you really want to do this sequentially, the easiest way is to perform your tasks recursively, actually invoking the next task in the completion handler of the prior one:
processNext(in: tableView.selectedRowIndexes) {
// do something when they're all done
}
Where:
func processNext(in rows: [Int], completion: @escaping () -> Void) {
guard let row = rows.first else {
completion()
return
}
myData.fetch(url: urlList[row]) { res in
self.anotherAsyncCall(res) { data in
//continue to deal with next row now
self.processNext(in: Array(rows.dropFirst()), completion: completion)
}
}
}
But I agree with GoodSp33d that the other approach is to wrap this asynchronous process in a custom, asynchronous, Operation
subclass.
But this begs the question why you want to do these sequentially. You will pay a significant performance penalty because of the inherent network latency for each request. So the alternative is to let them run concurrently, and use dispatch group to know when they're done:
let group = DispatchGroup()
tableView.selectedRowIndexes.forEach { row in
group.enter()
myData.fetch(url: urlList[row]) { res in
self.anotherAsyncCall(res) { data in
//continue to deal with next row now
group.leave()
}
}
}
group.notify(queue: .main) {
// do something when they're all done
}
Whether you can run these concurrently (or to what degree) is a function of what you're doing inside various asynchronous methods. But I would suggest you think hard about making this work concurrently, as the performance is likely to be much better.
Upvotes: 1
Reputation: 3867
If you are using some promise library, just use the all
function.
Here is some Document about promise.all()
And PromiseKit use when
instead,
you can read about the faq and the tutorial about when for more information.
If you want to do that without any promise library, here is the pseudocode:
var results = []
rows.forEach {row in
fetch(row) {res in
results.push(res)
if(results.length == rows.length) {
// do something using the results here
}
}
}
Upvotes: 0