Esperando hasta que termine la tarea

Resuelto Bartosz Woźniak asked hace 7 años • 7 respuestas

¿Cómo podría hacer que mi código espere hasta que DispatchQueuefinalice la tarea? ¿Necesita algún controlador de finalización o algo así?

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.

}

Estoy usando Xcode 8.2 y escribiendo en Swift 3.

Bartosz Woźniak avatar Feb 27 '17 18:02 Bartosz Woźniak
Aceptado

Si necesita ocultar la naturaleza asincrónica de myFunctionla persona que llama, use DispatchGroups para lograrlo. De lo contrario, utilice un bloque de finalización. Encuentre muestras para ambos a continuación.


Muestra de grupo de despacho

Puedes recibir una notificación cuando las llamadas enter()y el grupo leave()estén equilibrados:

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)
    }
}

o puedes esperar:

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
}

Nota : group.wait()bloquea la cola actual (probablemente la cola principal en su caso), por lo que debe hacerlo dispatch.asyncen otra cola (como en el código de muestra anterior) para evitar un punto muerto .


Muestra de bloque de finalización

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)")
}
shallowThought avatar Feb 27 '2017 11:02 shallowThought

En Swift 3, no es necesario un controlador de finalización cuando DispatchQueuefinaliza una tarea. Además puedes lograr tu objetivo de diferentes maneras.

Una forma es esta:

    var a: Int?

    let queue = DispatchQueue(label: "com.app.queue")
    queue.sync {

        for  i in 0..<10 {

            print("Ⓜ️" , i)
            a = i
        }
    }

    print("After Queue \(a)")

Esperará hasta que finalice el ciclo, pero en este caso su hilo principal se bloqueará.

También puedes hacer lo mismo así:

    let myGroup = DispatchGroup()
    myGroup.enter()
    //// Do your task

    myGroup.leave() //// When your task completes
     myGroup.notify(queue: DispatchQueue.main) {

        ////// do your remaining work
    }

Una última cosa: si desea utilizar completeHandler cuando su tarea se complete usando DispatchQueue, puede usar DispatchWorkItem.

Aquí hay un ejemplo de cómo usarlo DispatchWorkItem:

let workItem = DispatchWorkItem {
    // Do something
}

let queue = DispatchQueue.global()
queue.async {
    workItem.perform()
}
workItem.notify(queue: DispatchQueue.main) {
    // Here you can notify you Main thread
}
Usman Javed avatar Feb 27 '2017 11:02 Usman Javed