bleeckerj
bleeckerj

Reputation: 564

DispatchGroup in For Loop

So, I'm having a bit of a time trying to get DispatchGroup to keep a for loop from iterating before a long asynchronous operation has completed. Most examples I've found are fairly straight forward and clear, but I can't seem to get my simple test case to work as I would expect.

let group = DispatchGroup()

    for i in 1...3 {
        group.enter()
        print("INDEX \(i)")
        asynchronousOperation(index: i, completion: {
            print("HELLO \(i)")
            self.group.leave()

        })
        print("OUTSIDE \(i)")
    }


func asynchronousOperation(index: Int, completion: @escaping () -> ()) {
    DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+5) {
        print("DONE \(index)")
        completion()

    }
}

This ends up printing

START
INDEX 1
OUTSIDE 1
INDEX 2
OUTSIDE 2
INDEX 3
OUTSIDE 3
DONE 1
HELLO 1
DONE 2
HELLO 2
DONE 3
HELLO 3

I would've expected it to print something more like

START
INDEX 1
OUTSIDE 1
HELLO 1
INDEX 2
OUTSIDE 2
HELLO 2
INDEX 3
OUTSIDE 3
HELLO 3

Insofar as the next "INDEX" following an OUTSIDE wouldn't be printed until group.leave() had been called inside the asynchronousOperation()

Probably something simple I'm misunderstanding — any ideas?

Upvotes: 3

Views: 2487

Answers (2)

Kumar Reddy
Kumar Reddy

Reputation: 802

execute the below code to get the proper output:

for i in 1...3 {
    let semaphore = DispatchSemaphore(value: 0) // create a semaphore with a value 0. signal() will make the value 1.
    print("INDEX \(i)")
    asynchronousOperation(index: i, completion: {
        print("HELLO \(i)")
        semaphore.signal() // once you make the signal(), then only next loop will get executed.
    })
    print("OUTSIDE \(i)")
    semaphore.wait() // asking the semaphore to wait, till it gets the signal.
}

   func asynchronousOperation(index: Int, completion: @escaping () -> ()) {
    DispatchQueue.global().asyncAfter(deadline: DispatchTime.now()+5) {
        print("DONE \(index)")
        completion()
    }
}

Output :

INDEX 1 OUTSIDE 1 DONE 1 HELLO 1

INDEX 2 OUTSIDE 2 DONE 2 HELLO 2

INDEX 3 OUTSIDE 3 DONE 3 HELLO 3

Upvotes: 5

mugx
mugx

Reputation: 10105

In order to achieve that, If I understood correctly, you may want to use a DispatchSemaphore. Doing so:

let semaphore = DispatchSemaphore(value: 1)

for i in 1...3 {
   self.semaphore.wait()
   print("INDEX \(i)")
   asynchronousOperation(index: i, completion: {
     print("HELLO \(i)")
     self.semaphore.signal()
   })
   print("OUTSIDE \(i)")
}


func asynchronousOperation(index: Int, completion: @escaping () -> ()) {
  DispatchQueue.global().asyncAfter(deadline: DispatchTime.now()+5) {
    print("DONE \(index)")
    completion()
  }
}

Upvotes: 3

Related Questions