144 votes

Attendre que la tâche se termine

Comment puis-je faire en sorte que mon code attende que la tâche dans DispatchQueue se termine ? Faut-il un CompletionHandler ou autre ?

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

}

J'utilise Xcode 8.2 et j'écris en Swift 3.

306voto

shallowThought Points 11731

Utilisez DispatchGroup pour y parvenir. Vous pouvez soit recevoir une notification lorsque le groupe enter() et leave() les appels sont équilibrés :

func myFunction() {
    var a: Int?

    let group = DispatchGroup()
    group.enter()

    DispatchQueue.main.async {
        a = 1
        group.leave()
    }

    // does not wait. But the code in notify() gets run 
    // after enter() and leave() calls are balanced

    group.notify(queue: .main) {
        print(a)
    }
}

ou vous pouvez attendre :

func myFunction() {
    var a: Int?

    let group = DispatchGroup()
    group.enter()

    // avoid deadlocks by not using .main queue here
    DispatchQueue.global(attributes: .qosDefault).async {
        a = 1
        group.leave()
    }

    // wait ...
    group.wait()

    print(a) // you could also `return a` here
}

Note : group.wait() bloque la file d'attente actuelle (probablement la file principale dans votre cas), vous devez donc dispatch.async sur une autre file d'attente (comme dans l'exemple de code ci-dessus) pour éviter une impasse .

0 votes

Je veux exécuter une fonction dans une autre classe, mais je veux attendre la fin de cette fonction et ensuite continuer dans la classe actuelle, comment puis-je faire ?

3 votes

@SaeedRahmatolahi : Soit vous utilisez le wait (si vous n'avez pas de problème de blocage, c'est-à-dire si vous n'êtes pas sur le thread principal) ou fournir un gestionnaire d'achèvement ou utiliser l'approche notify dans votre classe appelante.

1 votes

Pourquoi vous appelez group.enter en dehors du bloc asynchrone ? Ne devrait-il pas être de la responsabilité de chaque bloc d'entrer et de sortir du groupe ?

33voto

Usman Javed Points 1397

Dans Swift 3, il n'y a pas besoin de gestionnaire de complétion lorsque DispatchQueue termine une tâche. En outre, vous pouvez atteindre votre objectif de différentes manières

Voici une façon de le faire :

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

Il attendra que la boucle se termine, mais dans ce cas, votre fil principal se bloquera.

Vous pouvez aussi faire la même chose comme ceci :

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

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

        ////// do your remaining work
    }

Une dernière chose : si vous voulez utiliser completionHandler lorsque votre tâche se termine en utilisant DispatchQueue, vous pouvez utiliser DispatchWorkItem .

Voici un exemple d'utilisation 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
}

2 votes

Je les ai toutes essayées et aucune n'a fonctionné en essayant de gérer les appels de firebase dans une boucle for.

2 votes

Cette solution, bien que visuellement élégante, ne gère pas les scénarios réalistes où du code hors de votre contrôle est exécuté de manière asynchrone (c'est-à-dire les callbacks de firebase comme indiqué dans le premier commentaire). Vous aurez besoin de .wait() pour gérer des situations plus complexes.

24voto

Saikrishna Rao Points 17

Version Swift 5 de la solution

func myCriticalFunction() {
    var value1: String?
    var value2: String?

    let group = DispatchGroup()

    group.enter()
    //async operation 1
    DispatchQueue.global(qos: .default).async { 
        // Network calls or some other async task
        value1 = //out of async task
        group.leave()
    }

    group.enter()
    //async operation 2
    DispatchQueue.global(qos: .default).async {
        // Network calls or some other async task
        value2 = //out of async task
        group.leave()
    }

    group.wait()

    print("Value1 \(value1) , Value2 \(value2)") 
}

3voto

Sahil Arora Points 50

Utiliser le groupe d'envoi

dispatchGroup.enter()
FirstOperation(completion: { _ in
    dispatchGroup.leave()
})
dispatchGroup.enter()
SecondOperation(completion: { _ in
    dispatchGroup.leave()
})
dispatchGroup.wait() // Waits here on this thread until the two operations complete executing.

7 votes

En supposant que vous l'appelez sur la file d'attente principale, cela provoquera un blocage.

0 votes

@shallowThought C'est tellement vrai.

0 votes

Je veux exécuter une fonction dans une autre classe, mais je veux attendre la fin de cette fonction et ensuite continuer dans la classe actuelle, comment puis-je faire ?

-1voto

codeByThey Points 805

Swift 4

Vous pouvez utiliser la fonction Async pour ces situations. Lorsque vous utilisez DispatchGroup() Parfois impasse peut se produire.

var a: Int?
@objc func myFunction(completion:@escaping (Bool) -> () ) {

    DispatchQueue.main.async {
        let b: Int = 3
        a = b
        completion(true)
    }

}

override func viewDidLoad() {
    super.viewDidLoad()

    myFunction { (status) in
        if status {
            print(self.a!)
        }
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X