suspend function: 概要、構文、コルーチンでの動作

著者: IT Sectr 公開日: 2026-06-22 読了時間: 9 分

Suspend functionは、suspend修飾子を持つ関数で、スレッドをブロックせずに実行を一時停止し、後で同じコルーチン内で再開できます。JetBrains Kotlin Docs, 2025によると、suspend関数はコルーチンの基本的な構成要素であり、コールバックなしで非同期処理を提供します。各suspend関数はContinuationに基づくステートマシンにコンパイルされ、一時停止ポイントを効率的に管理できます。

重要ポイント

  • Suspend — 関数を一時停止可能(非同期)としてマークするKotlinのキーワード
  • Continuation — コンパイラが状態を保存するために各suspend関数に追加する隠しパラメータ
  • 一時停止ポイント — 他のsuspend関数が呼び出される場所で、コルーチンがブロックせずに停止できる
  • ステートマシン — suspend関数の内部表現で、各一時停止ポイントが個別の状態
  • コルーチンからのみ呼び出し可能 — suspend関数は別のsuspend関数またはlaunch/asyncからのみ呼び出せる

Kotlinのsuspend functionとは?

Suspend functionは、suspendキーワードで宣言された関数で、スレッドをブロックせずに1つ以上のポイントで実行を一時停止できます。別のsuspend関数内でのsuspend関数の各呼び出しは、潜在的な一時停止ポイントです。

kotlin
suspend fun fetchUserData(): User {
    val response = httpClient.get("/user")
    return parser.parse(response)
}

Kotlinコンパイラはそのような関数をステートマシンに変換します。各一時停止ポイント(別のsuspend関数の呼び出し)は状態(label)になります。現在のスレッドは状態間で解放され、待機中の操作が完了すると、次の状態から実行が再開されます。

歴史

suspend関数はKotlin 1.3(2018年)でコルーチンとともに実験的機能として登場し、Kotlin 1.5(2021年)で安定化されました。それ以前は、Kotlin/Javaでの非同期処理はコールバック、RxJava、CompletableFutureを通じて実現されていました。suspend関数は、直線的な構文と自動スレッド管理を備えた代替手段を提供しました。

suspend関数の仕組み:Continuationとステートマシン

suspend関数の内部動作を理解することは、コルーチンを正しく扱うための鍵です。通常の関数とは異なり、各suspend関数はContinuationインターフェースを持つクラスにコンパイルされます。

Continuation — 隠しパラメータ

Kotlinコンパイラは、各suspend関数のパラメータリストの最後にContinuation型のパラメータを追加します。Continuationには以下が含まれます:

  • context — CoroutineContext(ディスパッチャ、ジョブ、コンテキスト要素)
  • resumeWith — 結果または例外で実行を再開するメソッド
  • label — ステートマシン内の現在の状態のインデックス

ステートマシンの例

他のsuspend関数への2つの呼び出しを持つsuspend関数があるとします:

kotlin
suspend fun process() {
    val a = stepOne()
    val b = stepTwo(a)
    println(b)
}

コンパイラはそれをラベル付きのステートマシンに変換します:

kotlin
// Simplified generated code representation
fun process(cont: Continuation<Unit>): Any? {
    val cont = cont as ProcessContinuation
    when (cont.label) {
        0 -> {
            cont.label = 1
            if (stepOne(cont) == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED
        }
        1 -> {
            cont.label = 2
            val a = cont.result as TypeA
            if (stepTwo(a, cont) == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED
        }
        2 -> {
            println(cont.result)
            Unit
        }
    }
}

重要な観察:関数がCOROUTINE_SUSPENDEDを返す場合、現在のスレッドは解放されます。非同期操作が完了すると、Continuation.resumeWithが呼び出され、ステートマシンは次のラベルから続行します。

suspend関数の構文:宣言と呼び出し

suspend関数の宣言は、funの前のsuspendキーワードを除いて、通常の関数と変わりません。制限は1つだけです:suspend関数はコルーチンまたは別のsuspend関数からのみ呼び出せます。

基本宣言

kotlin
suspend fun delayAndReturn(ms: Long): String {
    delay(ms)
    return "Done after ${ms}ms"
}

この例では、delayもsuspend関数であり、スレッドをブロックせずに指定されたミリ秒間コルーチンを一時停止します。遅延後、実行が再開されます。

コルーチンからの呼び出し

kotlin
fun main() = runBlocking {
    val result = delayAndReturn(1000)
    println(result)
}

runBlockingは通常の世界とコルーチンの間の橋渡しをします。ラムダ内では、任意のsuspend関数を呼び出すことができます。

suspendラムダと関数型

Kotlinは関数型のsuspendバージョンをサポートしています — suspend () -> T および suspend (A) -> B。これにより、高階関数に非同期ラムダを渡すことができます:

kotlin
suspend fun  withRetry(
    retries: Int = 3,
    block: suspend () -> T
): T {
    repeat(retries - 1) {
        try { return block() }
        catch (_: Exception) { delay(100) }
    }
    return block()
}

withRetry関数はsuspendラムダを受け取り、エラー時にその実行を再試行します。これはリトライ付きネットワークリクエストの典型的なパターンです。

suspend関数と通常の関数の違い

suspend関数と通常の関数の違いは、単に修飾子を追加するだけにとどまりません。主な違いを見てみましょう。

特性通常の関数suspend関数
実行スレッド完了までスレッドをブロックスレッドを解放して後で再開可能
コンパイラパラメータ指定されたパラメータのみ末尾に暗黙のContinuation
通常の関数からの呼び出し不可
スタック物理スレッドスタックヒープ上のステートマシン + ポイント間の物理スタック
戻り値直接値値またはCOROUTINE_SUSPENDED
パフォーマンス最小限のオーバーヘッドステートマシンあたり約数ナノ秒(Kotlin 1.9+)

なぜsuspend関数は通常の関数から呼び出せないのか

通常の関数にはContinuationがありません — 状態を保存する場所も、実行を再開する手段もありません。通常の関数からsuspend関数を呼び出す必要がある場合は、runBlocking(テスト用)またはCoroutineScope.launch(ライフサイクルを考慮した本番用)を使用してください。

Androidでのsuspend関数の例

Kotlinを使用したAndroidアプリでのsuspend関数の3つの実際のシナリオを見てみましょう。

例1:suspendクエリを使用するRoom DAO

Roomはsuspend関数を直接サポートしています — クエリは自動的にバックグラウンドスレッドで実行されます:

kotlin
@Dao
interface UserDao {
    @Query("SELECT * FROM users WHERE id = :id")
    suspend fun getUser(id: Int): User?

    @Insert
    suspend fun insertUser(user: User)
}

Roomは内部でDispatchers.IOを使用してクエリを実行し、結果はsuspend関数が呼び出されたディスパッチャで返されます。

例2:画面読み込みのためのsuspend関数の合成

kotlin
class ProfileViewModel : ViewModel() {
    private val repo = ProfileRepository()

    fun loadProfile(id: String) {
        viewModelScope.launch {
            val profile = repo.getProfile(id)
            _profile.value = profile
        }
    }
}

ViewModelScope.launchはコルーチンを作成し、その内部でgetProfile suspend関数が呼び出されます。結果を取得した後、UIはメインスレッドで更新されます。

例3:順次非同期ステップ

kotlin
suspend fun placeOrder(cart: Cart): OrderResult {
    val validated = validateCart(cart)
    val payment = processPayment(validated)
    val receipt = sendReceipt(payment)
    return receipt
}

3つのsuspend関数が順次実行されます。各ステップで、コルーチンはスレッドをブロックせずに一時停止できます。いずれかのステップが例外をスローした場合、残りのステップは実行されず、不正な注文状態から保護します。

suspend関数でよくある間違い

経験豊富なKotlin開発者でも、suspend関数を設計する際に間違いを犯します。最も一般的なものを見てみましょう。

間違い1:suspend内でのブロッキング呼び出し

suspend関数はコードを自動的に非同期にするわけではありません。Thread.sleep()、InputStream.read()、その他のブロッキング呼び出しは依然としてスレッドをブロックします。ブロッキング操作をラップするにはwithContext(Dispatchers.IO)を使用してください。

間違い2:不必要なsuspend関数の作成

関数が他のsuspend関数を呼び出さず、非同期操作も実行しない場合 — suspend修飾子は冗長です。ステートマシンのオーバーヘッドが追加され、呼び出しコンテキストが制限されます。関数が実際に一時停止する場合にのみsuspendにしてください。

間違い3:CancellationExceptionの無視

コルーチンがキャンセルされると、suspend関数はCancellationExceptionをスローします。それを無造作にキャッチしないでください — 呼び出し元のコードがキャンセルを適切に完了する能力を奪うことになります。最終処理操作を実行する必要がある場合は、finallyブロックとNonCancellableを使用してください。

kotlin
suspend fun safeOperation() {
    try {
        doWork()
    } finally {
        withContext(NonCancellable) {
            cleanup()
        }
    }
}

finallyブロックはキャンセル時も含めて常に実行され、NonCancellableはクリーンアップが中断されないことを保証します。

間違い4:コールバックからのsuspend関数の呼び出し

コルーチンを作成せずに、コールバックから直接suspend関数を呼び出すことはできません。コールバックスタイルをコルーチンに適応させるには、suspendCoroutineまたはsuspendCancellableCoroutineを使用してください。

よくある質問

suspend関数に一時停止ポイントがなくてもよいですか?

はい、技術的にはsuspend関数は他のsuspend関数を呼び出さなくても構いません。コンパイラは単一の状態(label 0)を持つステートマシンを作成します。しかし、そのような関数に実用的な利点はありません — 通常の関数のように実行されますが、オーバーヘッドがあります。不必要にsuspendを使用しないでください。

suspend関数をデバッグするには?

Kotlinはkotlinx-coroutines-debugを提供しています — DebugProbesとコルーチントレーシングツールを備えたライブラリです。Arctic Fox以降のAndroid Studioでは、デバッガに組み込みのCoroutinesタブがあり、アクティブなコルーチン、その状態、一時停止ポイントを表示します。

一時停止ポイントの数はパフォーマンスに影響しますか?

各一時停止ポイントはステートマシンに新しい状態を作成します。ほとんどのアプリケーションでは、1つのポイントのオーバーヘッドは数ナノ秒です(Kotlin 1.9+)。ループ内に数万のポイントがある場合のみ、操作を結合するかsequence/flowの使用を検討すべきです。

suspend関数は他の言語のasync/awaitとどう違いますか?

Kotlinでは、suspendは関数型の修飾子であり、戻り値のマーカー(C#のasyncなど)ではありません。任意のsuspend関数は任意のパラメータと戻り型を持つことができ、その呼び出しは構文的に通常の関数呼び出しと変わりません — 呼び出し場所にawait演算子はありません。

コールバック関数をsuspendに変換するには?

適応にはsuspendCancellableCoroutineを使用します。内部で、continuation.resume()を呼び出すコールバックを登録し、コールバックが購読解除をサポートする場合はキャンセレーショントークンを返します。これは古いAndroid APIをラップする標準的なパターンです。

まとめ

  • Suspend function — Continuationメカニズムを通じてスレッドをブロックせずに実行を一時停止できるsuspend修飾子付きの関数
  • ステートマシン — Kotlinバイトコードでのsuspend関数の内部表現。各一時停止ポイントはラベル付きの個別の状態
  • Continuation — コルーチンコンテキストと実行再開のためのresumeWithメソッドを含む隠しパラメータ
  • コルーチンからのみ呼び出し可能 — suspend関数はrunBlockingまたはCoroutineScopeなしでは通常の関数からアクセス不可
  • ブロッキング操作をsuspend内で行うにはwithContext(Dispatchers.IO)が必要 — そうしないとスレッドがブロックされる
  • RoomとRetrofitはsuspend関数をネイティブにサポートし、バックグラウンドスレッドを自動管理
  • CancellationException — finally + NonCancellableでキャンセルを処理し、CancellationExceptionを無造作にキャッチしない

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください