Andrey M.
Andrey M.

Reputation: 3069

How to wait until all NSOperations is finished?

I have the following code:

func testFunc(completion: (Bool) -> Void) {
    let queue = NSOperationQueue()
    queue.maxConcurrentOperationCount = 1

    for i in 1...3 {
        queue.addOperationWithBlock{
            Alamofire.request(.GET, "https://httpbin.org/get").responseJSON { response in
                switch (response.result){
                case .Failure:
                    print("error")
                    break;
                case .Success:
                    print("i = \(i)")
                }
            }
        }
        //queue.addOperationAfterLast(operation)
    }
    queue.waitUntilAllOperationsAreFinished()
    print("finished")
}

and output is:

finished
i = 3
i = 1
i = 2

but I expect the following:

i = 3
i = 1
i = 2
finished

So, why queue.waitUntilAllOperationsAreFinished() don't wait?

Upvotes: 3

Views: 2823

Answers (3)

Abhijith
Abhijith

Reputation: 3394

To check whether all operations finished - We could use KVO to observe number of operations in the Queue. Unfortunately both operations and operationCount are currently deprecated..!

So it's safe to use following option using dependency.

To check few operations are finished - Use Dependencies :

Create a final operation called "finishOperation" then add dependencies to all other required operation. This way, "finishOperation" will be executed only when depended operations are finished. Check this answer for code sample.

Upvotes: 1

Ozgur Vatansever
Ozgur Vatansever

Reputation: 52093

Each operation you've added into queue is immediately executed because Alamofire.request simply returns without waiting for the response data.

Furthermore, there is a possibility of deadlock there. Since responseJSON block is executed within the main queue by default, blocking the main thread by calling waitUntilAllOperationsAreFinished will prevent it from executing the completion block at all.

First, in order to fix the deadlock issue, you can tell Alamofire to execute the completion block in a different queue, second, you can use dispatch_group_t to group the number of asynchronous HTTP requests and keep the main thread waiting till all those requests in the group finish executing:

let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)
let group = dispatch_group_create()
for i in 1...3 {
  dispatch_group_enter(group)
  Alamofire.request(.GET, "https://httpbin.org/get").responseJSON(queue: queue, options: .AllowFragments) { response in
    print(i)
    dispatch_async(dispatch_get_main_queue()) {
      // Main thread is still blocked. You can update the UI here but it will take effect after all HTTP requests are finished.
    }
    dispatch_group_leave(group)
  }
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
print("finished")

Upvotes: 5

BlackM
BlackM

Reputation: 4065

I would suggest you to use KVO and observe when the queue has finish all the task instead of blocking the current thread until all the operations finished. Or you can use dependencies. Take a look at this SO question

Upvotes: 2

Related Questions