22

Comment faire pour que mon code attende jusqu'à la fin de la tâche dans DispatchQueue? A-t-il besoin de CompletionHandler ou quelque chose?Attente jusqu'à la fin de la tâche

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 écrire à Swift 3.

Répondre

59

Utilisez DispatchGroup s pour y parvenir. Vous pouvez être averti lorsque les appels enter() et leave() du groupe 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 (et retour):

func myFunction() -> Int? { 
    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() 

    // ... and return as soon as "a" has a value 
    return a 
} 

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

+0

Je veux exécuter une fonction dans une autre classe mais je veux attendre pour terminer cette fonction et puis dans la classe actuelle continuer comment puis-je gérer cela? –

+1

@SaeedRahmatolahi: utilisez l'approche wait (si vous ne bloquez pas le problème, c'est-à-dire si vous n'êtes pas sur le thread principal) ou fournissez un gestionnaire d'achèvement ou utilisez l'approche notify dans votre classe d'appel. – shallowThought

+0

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

1

Utilisez groupe d'expédition

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. 
+1

En supposant que vous appelez cela sur la file d'attente principale, cela entraînera une impasse. – shallowThought

+0

@shallowThought Tellement vrai. – Prateekro

+0

Je veux exécuter une fonction dans une autre classe mais je veux attendre pour terminer cette fonction et puis dans la classe actuelle continuer comment puis-je gérer cela? –

11

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

Une façon est la suivante.

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 jusqu'à ce que la boucle se termine, mais dans ce cas votre thread de messagerie se bloquera.

Vous pouvez également faire cette même chose comme

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 souhaitez utiliser completionHandler lorsque votre tâche se termine à l'aide de DispatchQueue, vous pouvez utiliser DispatchWorkItem.

Voici un exemple comment utiliser 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 
}