言語
日本語
English

Caution

お使いのブラウザはJavaScriptが無効になっております。
当サイトでは検索などの処理にJavaScriptを使用しています。
より快適にご利用頂くため、JavaScriptを有効にしたうえで当サイトを閲覧することをお勧めいたします。

Kotlin辞典

  1. トップページ
  2. Kotlin辞典
  3. await() / Deferred

await() / Deferred

対応: Kotlin 1.3(2018)

Kotlinコルーチンの『Deferred<T>』は非同期処理の結果を保持するオブジェクトで、『await()』を呼ぶと結果が出るまでコルーチンを中断して待機します。

構文

// async で Deferred を取得
val deferred: Deferred<型> = async {
    // 非同期処理
    結果 // 戻り値
}

// await で結果を取得(コルーチン内でのみ呼び出せます)
val result: 型 = deferred.await()

// awaitAll で複数の Deferred を一括待機
val results: List<型> = awaitAll(deferred1, deferred2, deferred3)

メソッド一覧

メソッド / プロパティ概要
deferred.await()結果が出るまでコルーチンを中断して結果を返します。
awaitAll(vararg deferred)複数の Deferred を並行待機して結果のリストを返します。
deferred.isCompleted処理が完了しているか確認します。
deferred.isCancelledキャンセルされているか確認します。
deferred.cancel()非同期処理をキャンセルします。
deferred.getCompleted()完了済みの場合に結果を取得します(未完了なら例外)。

サンプルコード

sample_await_deferred.kt
import kotlinx.coroutines.*

suspend fun fetchUser(id: Int): String {
    delay(200)
    return "User-$id"
}

suspend fun fetchScore(id: Int): Int {
    delay(150)
    return id * 100
}

fun main() = runBlocking {
    // 2つの非同期処理を並行して起動する
    val userDeferred = async { fetchUser(1) }
    val scoreDeferred = async { fetchScore(1) }

    // それぞれの結果を await で取得する
    val user = userDeferred.await()
    val score = scoreDeferred.await()
    println("$user: ${score}点") // User-1: 100点

    // awaitAll で複数の結果を一括取得する
    val deferreds = (1..3).map { id ->
        async { fetchUser(id) }
    }
    val users = awaitAll(*deferreds.toTypedArray())
    println(users) // [User-1, User-2, User-3]

    // isCompleted で完了チェック
    val d = async { delay(500); "done" }
    println(d.isCompleted) // false(まだ実行中)
    d.await()
    println(d.isCompleted) // true
}
await_deferred.kt
kotlinc await_deferred.kt -include-runtime -d await_deferred.jar
java -jar await_deferred.jar
# ※ kotlinx-coroutines-core ライブラリが必要です(kotlinc 単体では実行不可)
User-1: 100点
[User-1, User-2, User-3]
false
true

よくあるミス

NG例1: 『async』 の直後にすぐ 『await()』 を呼ぶと順次実行になる。各 async が完了を待ってから次に進むためです。

mistake1_ng.kt
import kotlinx.coroutines.*

fun main() = runBlocking {
    val start = System.currentTimeMillis()
    val r1 = async { delay(100); "呪術師: 五条悟" }.await() // 100ms 待つ
    val r2 = async { delay(100); "呪術師: 虎杖悠仁" }.await() // さらに 100ms 待つ
    println("${System.currentTimeMillis() - start}ms") // ~200ms(順次)
}

コンパイルして実行すると次のようになります。

kotlinc mistake1_ng.kt -include-runtime -d mistake1_ng.jar
java -jar mistake1_ng.jar
# ※ kotlinx-coroutines-core ライブラリが必要です(kotlinc 単体では実行不可)
207ms
mistake1_ok.kt
// async を全部起動してから await する(並行実行)
import kotlinx.coroutines.*

fun main() = runBlocking {
    val start = System.currentTimeMillis()
    val d1 = async { delay(100); "呪術師: 五条悟" }
    val d2 = async { delay(100); "呪術師: 虎杖悠仁" }
    val d3 = async { delay(100); "呪術師: 伏黒恵" }
    println(d1.await())
    println(d2.await())
    println(d3.await())
    println("${System.currentTimeMillis() - start}ms") // ~100ms(並行)
}

コンパイルして実行すると次のようになります。

kotlinc mistake1_ok.kt -include-runtime -d mistake1_ok.jar
java -jar mistake1_ok.jar
# ※ kotlinx-coroutines-core ライブラリが必要です(kotlinc 単体では実行不可)
呪術師: 五条悟
呪術師: 虎杖悠仁
呪術師: 伏黒恵
104ms

NG例2: 未完了の 『Deferred』 に 『getCompleted()』 を呼ぶと 『IllegalStateException』 が発生する。

import kotlinx.coroutines.*

fun main() = runBlocking {
    val d = async { delay(300); "完了" }
    println(d.getCompleted()) // IllegalStateException: This job has not completed yet
}

修正後は次の通りです。

// await で完了を待ってから getCompleted を呼ぶ
import kotlinx.coroutines.*

fun main() = runBlocking {
    val d = async { delay(300); "完了" }
    d.await()
    println(d.getCompleted()) // 完了
}

NG例3: 『await()』 をコルーチン外(通常の関数)で呼ぶとコンパイルエラーになる。

import kotlinx.coroutines.*

fun fetchResult(d: Deferred<String>): String {
    return d.await() // コンパイルエラー: Suspend function 'await' should be called only from a coroutine or another suspend function
}

修正後は次の通りです。

// suspend 関数として定義する
import kotlinx.coroutines.*

suspend fun fetchResult(d: Deferred<String>): String {
    return d.await() // suspend 関数内なので呼び出せる
}

概要

複数の『async』を起動してから後でまとめて『await()』を呼ぶことで並列実行が実現します。『async』直後に『await()』を呼ぶと順次実行になるため注意してください。

複数の Deferred をまとめて待つには『awaitAll()』が便利です。いずれか1つが例外を投げると他もキャンセルされます。

コルーチンの起動方法はlaunch / asyncを、タイムアウトはdelay() / withTimeout()を参照してください。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。