1

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
        }
    }
}
1
  • 2
    If the result of first call is not related to result of another call you can always make use of available concurrent APIs. Of top of my head I can think of utilizing 1. NSOperation. You can create multiple operations and add them to a queue. Based on availability of system resources multiple operations would get picked up by queue. 2. DispatchGroup Much simpler way to achieve the same, if you are not looking for granular control. Commented Jan 10, 2018 at 4:10

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

Comments

0

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
        }
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.