2

I need the performAsyncRequests-methods (for each i) to finish before the next (i + 1) is being executed. I'm trying this:

let group = DispatchGroup()
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1

for i in 0..<results.count {
    group.enter()
    print("\(i)-async started")
    queue.addOperation {
        print("Operation \(i) added")
        self.performAsyncRequests(completion: { (result) in
            print("Request \(i) completed")
            group.leave()
        })
    }
}
group.notify(queue: .main) {
    print("All requests completed")
    self.reloadTableView()
}

But this executes several requests concurrently. Appreciate if someone could point me in the right direction.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Dan Abnormal
  • 1,188
  • 11
  • 21
  • By the way, do you really want to perform these requests sequentially? You pay a serious performance penalty if you do that. Usually if doing multiple requests, we like to do them concurrently, perhaps constraining the degree of concurrency to 4 or 5 at a time. – Rob Mar 09 '19 at 17:30
  • Thanks @Rob. It seems like the best way to me right now, based on the logic I’ve built. In each request I grab some values that I set to global properties. I need a way to keep these sets of properties separate from the others. Don’t know if this makes sense – Dan Abnormal Mar 09 '19 at 17:58
  • OK, but just appreciate that this approach is much slower than allowing concurrent requests. Each network request introduces a little latency, and when you force sequential series of requests, these latencies really start to add up. But do what you have to... – Rob Mar 09 '19 at 18:15
  • You’ve almost convinced me to go with a diffrent approach. But since that involves rewriting a lot of code I figure it’s worth a look. Appreciate your help! – Dan Abnormal Mar 09 '19 at 18:21

2 Answers2

1

The easiest solution is to write a routine that recursively performs requests, e.g.:

func performNextRequest(_ index: Int = 0) {
    guard index < results.count else {
        DispatchQueue.main.async {
            // initiate whatever you want when this is done
            self.reloadTableView()
        }
        return
    }

    performAsyncRequest(index) { [weak self] in
        self?.performNextRequest(index + 1)
    }
}

The more elegant solution is to use some asynchronous pattern such as asynchronous Operation subclass or promises.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
0

DispatchGroup is the wrong API. I makes sure that the notify closure is not called before the last operation has finished regardless of the execution order.

A possible solution is an asynchronous Operation in conjunction with a non-concurrent queue.

vadian
  • 274,689
  • 30
  • 353
  • 361