Search code examples
swiftmultithreadingasynchronousgrand-central-dispatch

Waiting until the task finishes


How could I make my code wait until the task in DispatchQueue finishes? Does it need any completion handler or something?

func myFunction() {
    var a: Int?

    DispatchQueue.main.async {
        var b: Int = 3
        a = b
    }

    // Wait until the task finishes, then print.

    print(a) // This will contain nil, of course, because it
             // will execute before the code above.

}

I'm using Xcode 8.2 and writing in Swift 3.


Solution

  • If you need to hide the asynchronous nature of myFunction from the caller, use DispatchGroups to achieve this. Otherwise, use a completion block. Find samples for both below.


    DispatchGroup Sample

    You can either get notified when the group's enter() and leave() calls are balanced:

    func myFunction() {
        var a = 0
    
        let group = DispatchGroup()
        group.enter()
    
        DispatchQueue.main.async {
            a = 1
            group.leave()
        }
    
        // does not wait. But the code in notify() is executed 
        // after enter() and leave() calls are balanced
    
        group.notify(queue: .main) {
            print(a)
        }
    }
    

    or you can wait:

    func myFunction() {
        var a = 0
    
        let group = DispatchGroup()
        group.enter()
    
        // avoid deadlocks by not using .main queue here
        DispatchQueue.global(qos: .default).async {
            a = 1
            group.leave()
        }
    
        // wait ...
        group.wait()
        
        print(a) // you could also `return a` here
    }
    

    Note: group.wait() blocks the current queue (probably the main queue in your case), so you have to dispatch.async on another queue (like in the above sample code) to avoid a deadlock.


    Completion Block Sample

    func myFunction(completion: @escaping (Int)->()) {
        var a = 0
    
        DispatchQueue.main.async {
            let b: Int = 1
            a = b
            completion(a) // call completion after you have the result
        }
    }
    

    // on caller side:
    myFunction { result in
        print("result: \(result)")
    }