0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Kotlin】Kotlin Coroutinesで理解する「タイムアウト(Timeout)」

Last updated at Posted at 2025-10-16

概要

コルーチンを使うと、非同期処理を簡潔に書けるようになりますが、
「処理が終わらない」「通信が詰まる」 といったケースでは、
明示的にタイムアウト(Timeout) を設定して自動キャンセルすることが重要です。

Kotlinではこれを withTimeout() / withTimeoutOrNull() で実現します。


タイムアウトとは?

「一定時間内に完了しなければ、その処理をキャンセルする」仕組みです。

ネットワーク通信や重い計算など、いつ終わるかわからない処理でよく使われます。


基本構文

withTimeout(timeMillis: Long)

指定した時間を超えると 例外(TimeoutCancellationException をスローします。

import kotlinx.coroutines.*

fun main() = runBlocking {
    try {
        withTimeout(1300L) { // ← 1.3秒でタイムアウト
            repeat(1000) { i ->
                println("I'm working $i ...")
                delay(500L)
            }
        }
    } catch (e: TimeoutCancellationException) {
        println("Timeout! (${e.message})")
    }
}

実行結果:

I'm working 0 ...
I'm working 1 ...
I'm working 2 ...
Timeout! (Timed out waiting for 1300 ms)

ポイント:

  • タイムアウト発生時、ブロック内の処理はキャンセルされます。
  • 内部的には「キャンセル(Cancellation)」機構が使われています。
  • 親スコープにもキャンセルが伝播します。

withTimeoutOrNull を使う

例外を投げずに nullを返すだけで済ませたい場合はこちらが便利。

fun main() = runBlocking {
    val result = withTimeoutOrNull(1300L) {
        repeat(1000) { i ->
            println("I'm working $i ...")
            delay(500L)
        }
        "Done" // ← ここに到達しない
    }
    println("Result: $result")
}

出力:

I'm working 0 ...
I'm working 1 ...
I'm working 2 ...
Result: null

nullが返るので、try-catch不要。
例外制御を避けたい場合におすすめです。


内部動作:キャンセルとの関係

Kotlinの withTimeout() は内部的に次のように動きます:

つまり、タイムアウト = キャンセルの一種 です。
コルーチンがキャンセルに対応していれば、自動的に中断されます。


ネットワーク通信の例(Retrofit × Coroutines)

suspend fun fetchUserData(api: ApiService): User? {
    return withTimeoutOrNull(5_000L) { // 5秒でタイムアウト
        api.getUserInfo() // suspend関数
    }
}

通信が5秒を超えると自動的にキャンセルされ、nullが返ります。
これにより無限待ちのリスクを防げます。


タイムアウトを活用する場面

使用場面 説明
ネットワーク通信 APIの応答が遅い場合に自動中断
DBアクセス クエリが長引く場合の制御
重い計算処理 無限ループ防止
外部I/O処理 ファイル・ソケット操作の安全制御

タイムアウトとキャンセルの組み合わせ

複数ジョブを並列実行して、その中の一部だけにタイムアウトを設定することも可能です。

val job = launch {
    withTimeout(2000L) {
        repeat(100) { i ->
            println("Task $i running ...")
            delay(500L)
        }
    }
}

上記では job 自体の寿命は長くても、内部処理だけがタイムアウトします。


よくある落とし穴

問題 原因 対処
タイムアウトが効かない 処理がキャンセル非対応(isActiveを見ていない) 明示的にisActiveyield()を入れる
例外でアプリが落ちる withTimeout使用時にtry-catch未対応 withTimeoutOrNullを使う
親スコープまで止まる Structured Concurrencyで伝播 supervisorScopeで隔離可能

まとめ

メソッド 動作 例外 返り値
withTimeout() タイムアウト時にキャンセル TimeoutCancellationException なし
withTimeoutOrNull() タイムアウト時にnullを返す 発生しない null

KotlinのwithTimeoutは、
「協調的キャンセル」と「構造化並行性」の上に成り立つ強力な機能です。

アプリがハングするような不確実な処理に、
タイムアウトを明示的に設けることが信頼性向上の第一歩になります。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?