using dispatchgroup wait for task completion in for loop and exit from loop if failure

526 views Asked by At

I am trying to exit from for loop according to condition but I ran into the issue as it does not exit even from the loop. Here is a loop of my code.

var isFailure = true
let dispatchGroup = DispatchGroup()
var myFailureTask: Int?
for item in 1...5 {
  dispatchGroup.enter()
  test(item: item, completion: {
    print("Success\(item)")
    dispatchGroup.leave()
  }, failureBlock: {
    print("Failure\(item)")
    myFailureTask = item
    dispatchGroup.leave()
    return
  })
  dispatchGroup.wait()
}
dispatchGroup.notify(queue: .main) {
  if let myFailure = myFailureTask {
    print("task failure \(myFailure)")
  } else {
    print("all task done")
  }
}
func test(item: Int,completion: @escaping(() -> ()), failureBlock: @escaping(() -> ())) {
    Thread.sleep(forTimeInterval: TimeInterval(item))
  isFailure = !isFailure
  if isFailure {
    failureBlock()
  } else {
    completion()
  }
}
2

There are 2 answers

1
Tarun Tyagi On

The return returns from current scope.

In this case it's returning from the failureBlock: {} and NOT from the for loop scope.

You have to refactor the code to achieve what you are trying to do.

EITHER (in case this code is executing synchronously) you can return a success value true | false from the function by making function's return type Bool and removing the failureBlock argument.

OR (in case this code is executing asynchronously) you have to think of waiting on one task to complete/fail before triggering the other.

UPDATE

I think following might be a simplified version of this code -

var isFailure: Bool = false

func callTest(for item: Int) {
    print("task initiated \(item)")
    test(item: item, completion: {
        print("task succeeded \(item)")
        if item < 5 {
            callTest(for: item+1)
        } else {
            print("all tasks done")
        }
    }, failureBlock: {
        print("task failed \(item)")
    })
}

func test(item: Int, completion: @escaping (() -> Void), failureBlock: @escaping (() -> Void)) {
    Thread.sleep(forTimeInterval: TimeInterval(item))
    isFailure.toggle()
    if isFailure {
        failureBlock()
    } else {
        completion()
    }
}

callTest(for: 1)
1
Prashant Ghimire On
var isFailure = false
let dispatchGroup = DispatchGroup()
var myFailureTask: Int?
for item in 1...5 {
  dispatchGroup.enter()
  test(item: item, completion: {
    print("Success\(item)")
    dispatchGroup.leave()
  }, failureBlock: {
    print("Failure\(item)")
    myFailureTask = item
    dispatchGroup.leave()
  })
  if isFailure == true {
    break
  }
  dispatchGroup.wait()
}
dispatchGroup.notify(queue: .main) {
  if let myFailure = myFailureTask {
    print("task failure \(myFailure)")
  } else {
    print("all task done")
  }
}
func test(item: Int,completion: @escaping(() -> ()), failureBlock: @escaping(() -> ())) {
    Thread.sleep(forTimeInterval: TimeInterval(item))
  isFailure = !isFailure
  if isFailure {
    failureBlock()
  } else {
    completion()
  }
}```
Made few changes work like a charm.
Any one has better idea please comment