Menunggu hingga tugas selesai

99

Bagaimana saya bisa membuat kode saya menunggu hingga tugas di DispatchQueue selesai? Apakah itu membutuhkan CompletionHandler atau sesuatu?

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

}

Saya menggunakan Xcode 8.2 dan menulis di Swift 3.

Bartosz Woźniak
sumber

Jawaban:

229

Gunakan DispatchGroups untuk mencapai ini. Anda bisa mendapatkan pemberitahuan ketika grup enter()dan leave()panggilan seimbang:

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

atau Anda bisa menunggu:

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
}

Catatan : group.wait()memblokir antrian saat ini (mungkin antrian utama dalam kasus Anda), jadi Anda harus berada dispatch.asyncdi antrian lain (seperti pada contoh kode di atas) untuk menghindari kebuntuan .

shallowThought
sumber
Saya ingin menjalankan fungsi di kelas lain tetapi saya ingin menunggu untuk menyelesaikan fungsi itu dan kemudian di kelas saat ini melanjutkan bagaimana saya bisa mengatasinya?
Saeed Rahmatolahi
2
@SaeedRahmatolahi: Gunakan waitpendekatan (jika tidak ada masalah bagi Anda untuk memblokir, misalnya jika Anda tidak berada di thread utama) atau berikan penangan penyelesaian atau gunakan pendekatan notify di kelas pemanggil Anda.
shallowPikir
Mengapa Anda menelepon di group.enterluar blok asinkron? Bukankah seharusnya itu menjadi tanggung jawab setiap blok untuk masuk dan keluar dari grup?
Tagihan
4
@ Bill waitmenunggu sampai enterdan leavepanggilan seimbang. Jika Anda menempatkan enterdi penutupan, waittidak akan menunggu karena enterbelum disebut belum dan dengan demikian jumlah enterdan leavepanggilan yang seimbang (# masuk == 0, # leav == 0).
shallowThought
1
@rustyMagnet untuk pengujian ini kemungkinan besar bukan cara yang tepat. Gunakan XCTTestExpectations sebagai gantinya. Lihat kode contoh ini
shallowThought
26

Di Swift 3, tidak diperlukan penangan penyelesaian saat DispatchQueuemenyelesaikan satu tugas. Selanjutnya Anda dapat mencapai tujuan Anda dengan cara yang berbeda

Salah satu caranya adalah ini:

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

Ini akan menunggu sampai loop selesai tetapi dalam hal ini utas utama Anda akan diblokir.

Anda juga bisa melakukan hal yang sama seperti ini:

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

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

        ////// do your remaining work
    }

Satu hal terakhir: Jika Anda ingin menggunakan completeHandler saat tugas Anda selesai menggunakan DispatchQueue, Anda bisa menggunakan DispatchWorkItem.

Berikut adalah contoh bagaimana menggunakan 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
sumber
1
Saya mencoba semuanya dan tidak ada yang berhasil saat mencoba menangani panggilan firebase dalam loop for
2

Gunakan grup pengiriman

   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.
Sahil Arora
sumber
5
Dengan asumsi Anda memanggil ini di antrian utama, ini akan menyebabkan kebuntuan.
shallowPikir
@Hallow Pikir Begitu benar.
Prateekro
Saya ingin menjalankan fungsi di kelas lain tetapi saya ingin menunggu untuk menyelesaikan fungsi itu dan kemudian di kelas saat ini melanjutkan bagaimana saya bisa mengatasinya?
Saeed Rahmatolahi
1
Meskipun contoh di atas akan memblokir utas utama, seperti yang ditunjukkan jawaban sebelumnya, membungkus di dalam DispatchQueue.global().async{}tidak akan memblokir antrean utama.
JonnyB
2

Solusi cepat versi 5

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

}

Saikrishna Rao
sumber
1

Cepat 4

Anda dapat menggunakan Fungsi Async untuk situasi ini. Saat Anda menggunakan DispatchGroup(), Terkadang kebuntuan mungkin terjadi.

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!)
        }
    }
}
Ali Ihsan URAL
sumber