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.

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 .


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
}

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.

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

}


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!)
        }
    }
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.