はじめに
複数の非同期処理を走らせて、全ての処理が完了したタイミングで特定の処理を行った時の内容です。
これは、配列の中のそれぞれ値に対して、API通信を非同期に実行し、すべてが完了したタイミングでUIに反映させようとした時のお話です。
非同期処理
以下のような非同期処理を作成します。
ランダムな数値を生成することで、処理が完了するタイミングがバラバラになるように設定しています。
func asyncAfter(number: Int, completion: @escaping (_ number: Int, _ interval: Double) -> Void) {
print("#\(number) Start")
let interval = TimeInterval(arc4random() % 100 + 1) / 100
DispatchQueue.global().asyncAfter(deadline: .now() + interval) {
completion(number, interval)
}
}
非同期処理を並列に実行
まず、DispatchGroup()
を作成します。
そして、それぞれの値に対して非同期処理を実行する際に.enter()
を呼び、処理が完了したタイミングで.leave()
を呼びます。
そうすると、全ての処理が完了したタイミングで、.notify()
の中の処理が実行されるという仕組みです。
func asyncTask() {
let dispatchGroup = DispatchGroup()
for i in 0 ..< 5 {
dispatchGroup.enter()
asyncAfter(number: i) { (number: Int, interval: Double) -> Void in
defer { dispatchGroup.leave() }
print("#\(number) completed (interval: \(interval))")
}
}
dispatchGroup.notify(queue: .main) {
// 全ての処理が完了した時に呼ばれる
print("all process completed!")
}
}
出力結果
実際に実行してみると、出力結果は以下のようになりました。想定通りの挙動になってそうですね。
まず、for文によってそれぞれの値に対して非同期処理が、Start
します。
その後、インターバルの短い処理からcompleted
されていき、全ての処理が完了したタイミングで、.notify()
内の処理が呼ばれていることが分かります。
#0 Start
#1 Start
#2 Start
#3 Start
#4 Start
#0 completed (interval: 0.18)
#3 completed (interval: 0.57)
#4 completed (interval: 0.6)
#1 completed (interval: 0.67)
#2 completed (interval: 0.93)
all process completed!
コード全文
以下、コードの全文です。
func asyncTask() {
let dispatchGroup = DispatchGroup()
for i in 0 ..< 5 {
dispatchGroup.enter()
asyncAfter(number: i) { (number: Int, interval: Double) -> Void in
defer { dispatchGroup.leave() }
print("#\(number) completed (interval: \(interval))")
}
}
dispatchGroup.notify(queue: .main) {
// 全ての処理が完了した時に呼ばれる
print("all process completed!")
}
}
func asyncAfter(number: Int, completion: @escaping (_ number: Int, _ interval: Double) -> Void) {
print("#\(number) Start")
let interval = TimeInterval(arc4random() % 100 + 1) / 100
DispatchQueue.global().asyncAfter(deadline: .now() + interval) {
completion(number, interval)
}
}
参考