Skip to content

非同期処理と同期処理の違い

処理の実行方法は、非同期処理と同期処理の二つに分けられます。

同期処理では、呼び出した処理が終わるまで、呼び出し元は次の処理へ進みません。

同期処理。呼び出し元はloadData()の完了を待ち、結果が返ってからdataを表示して終了する。

print("開始")
let data = loadData()
print(data)
print("終了")

実行結果

開始
`data`
終了

loadData()が完了して結果が帰ってくるまで、後続のprintは実行されません。コードに書かれた順番と、処理が完了する順番が一致します。

非同期処理では、時間のかかる処理の完了をその場で待たず、呼び出し元はいったん次へ進みます。そして、処理が完了したら、その結果を受け取り後続のprintを実行します。

非同期処理。呼び出し元はloadData()を呼んですぐ終了し、データ取得の完了通知を受けてからdataを表示する。

print("開始")
loadData { data in
print(data)
}
print("終了")

実行結果

開始
終了
`data`

さらに複雑な例で、同期処理と非同期処理の違いを理解しましょう

次のSwiftUIアプリでは、ボタンを押すとloadDataSynchronously()が3秒間待機したのちに、結果を返します。この関数は同期関数なので、結果が返るまで次の処理へ進めません。

import SwiftUI
func loadDataSynchronously() -> String {
Thread.sleep(forTimeInterval: 3)
return "取得したデータ"
}
struct SynchronousView: View {
@State private var isLoading = false
@State private var message = "データはありません"
var body: some View {
VStack(spacing: 16) {
if isLoading {
ProgressView()
}
Text(message)
Button("データを読み込む") {
isLoading = true
message = loadDataSynchronously()
isLoading = false
}
}
}
}

loadDataSynchronously()は同期関数であるため、この関数が実行されているスレッドは後続の処理を行うことができません。そして、この関数が実行されているのはUIを処理するメインスレッドです。そのため、データを取得している3秒間は、画面を更新することができず、アプリ上はボタンを押した時点から全く動かなくなります。ローディングスピナーすら表示されません。3秒後にデータが帰ってから、画面が更新されます。

同期関数。ボタンを押すとメインスレッドが3秒間停止し、その間スピナーも表示されない。3秒後に画面が更新される。

次に、同じ処理を非同期関数として実装した例です。loadDataAsynchronously()は3秒後に結果を返します。しかし、その間は呼び出し元のメインスレッドを解放します。そして、処理が完了したら、処理結果をメインスレッドに通知します。

import SwiftUI
import Dispatch
func loadDataAsynchronously(
completion: @escaping @MainActor @Sendable (String) -> Void
) {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
completion("取得したデータ")
}
}
struct AsynchronousView: View {
@State private var isLoading = false
@State private var message = "データはありません"
var body: some View {
VStack(spacing: 16) {
if isLoading {
ProgressView()
}
Text(message)
Button("データを読み込む") {
isLoading = true
loadDataAsynchronously { data in
message = data
isLoading = false
}
}
}
}
}

今度は、データを待っている間もメインスレッドが止まりません。画面にはローディングスピナーが表示され、3秒後にデータを受け取ると、その内容が画面に表示されます。

非同期関数。ボタンを押したあともメインスレッドは画面を更新でき、スピナーが表示される。3秒後に結果が表示される。

この例では、GCDのasyncAfterを使って非同期処理を実装しました。次のページでは、Swift Concurrency以前の非同期処理の実装方法を解説します。