【Coroutinesガイド】suspend関数の構成

※ソース記事は[こちら](https://kotlinlang.org/docs/composing-suspending-functions.html)
このセクションではsuspend関数の構成に対する様々なアプローチについてカバーしている。
## デフォルトはシーケンシャル
リモートのサービスや計算のような、有益なことをする二つのsuspend関数がどこかに定義されていると仮定する。それらは単に有益なふりをしており、実際は次の例のため、それぞれ1秒遅延するだけとする。
```Kotlin
suspend fun doSomethingUsefulOne(): Int {
    delay(1000L) // ここで有益なことをするふりをする
    return 13
}

suspend fun doSomethingUsefulTwo(): Int {
    delay(1000L) // ここでも有益なことをするふりをする
    return 29
}
```
それらをシーケンシャル-最初は`doSomethingUsefulOne`、その後で`doSomethingUsefulTwo`-に呼び出しそれらの結果を合計する必要がある場合、どうするか？実際は、二つ目を呼び出す必要があるかどうか決めるため、あるいは呼び出し方を決めるために、最初の関数の結果を使う場合にそれを行う。
我々は通常のシーケンシャルな呼び出しを使う。なぜなら、コルーチンのコードは通常のコードとまったく似ており、デフォルトがシーケンシャルだからである。次の例は、両方のsuspend関数を実行するのにかかった合計の時間を計測するために、実際にやってみる。
```Kotlin
import kotlinx.coroutines.*
import kotlin.system.*

fun main() = runBlocking<Unit> {
    val time = measureTimeMillis {
        val one = doSomethingUsefulOne()
        val two = doSomethingUsefulTwo()
        println("The answer is ${one + two}")
    }
    println("Completed in $time ms")    
}

suspend fun doSomethingUsefulOne(): Int {
    delay(1000L) //  ここで有益なことをするふりをする
    return 13
}

suspend fun doSomethingUsefulTwo(): Int {
    delay(1000L) // ここでも有益なことをするふりをする
    return 29
}
```
これは次のようなものを生成する。
```
The answer is 42
Completed in 2017 ms
```
## asyncの使用による並列
`doSomethingUsefulOne`と`doSomethingUsefulTwo`呼び出しの間に依存関係がなく、両方を同時に実行することで、より早く答えが欲しい場合はどうするか？
そこは[async](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html)が役立つ所である。
概念的に[async](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html)は[launch](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/launch.html)にちょうど良く似ている。それは別のコルーチンを開始し、他のすべてのコルーチンと並列に動作する軽量なスレッドである。違いは、`launch`は[Job](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/index.html)を返却し、それは結果の値を持たない。一方`async`は[Deferred](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/index.html)を返却し、それは軽量なノンブロッキングなfutureであり、結果を後で提供するpromiseを表す。deferredの値について`.await()`を使うことで、最終的な結果を得ることができるが、`Deferred`は`Job`でもあるため、必要ならキャンセルすることができる。
```Kotlin
import kotlinx.coroutines.*
import kotlin.system.*

fun main() = runBlocking<Unit> {
    val time = measureTimeMillis {
        val one = async { doSomethingUsefulOne() }
        val two = async { doSomethingUsefulTwo() }
        println("The answer is ${one.await() + two.await()}")
    }
    println("Completed in $time ms")
}

suspend fun doSomethingUsefulOne(): Int {
    delay(1000L) // ここで有益なことをするふりをする
    return 13
}

suspend fun doSomethingUsefulTwo(): Int {
    delay(1000L) // ここでも有益なことをするふりをする
    return 29
}
```
これはこのような結果となる。
```
The answer is 42
Completed in 1017 ms
```
これは二倍速い。なぜなら二つのコルーチンが並列に実行されているためである。留意すべきは、コルーチンの並列性は常に明示的だということである。
## lazyに開始するasync
オプションで、[async](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html)は`start`パラメータに[CoroutineStart.LAZY](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-start/-l-a-z-y/index.html)をセットして作ることができる。このモードでは、結果が[await](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html)によって必要になるか、`Job`の[start](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/start.html)関数が呼び出されるときのみ、コルーチンが開始される。次の例を実行してみる。
```Kotlin
import kotlinx.coroutines.*
import kotlin.system.*

fun main() = runBlocking<Unit> {
    val time = measureTimeMillis {
        val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() }
        val two = async(start = CoroutineStart.LAZY) { doSomethingUsefulTwo() }
        // 何らかの計算
        one.start() // 最初のものを開始
        two.start() // 二番目のものを開始
        println("The answer is ${one.await() + two.await()}")
    }
    println("Completed in $time ms")    
}

suspend fun doSomethingUsefulOne(): Int {
    delay(1000L) // ここで有益なことをするふりをする
    return 13
}

suspend fun doSomethingUsefulTwo(): Int {
    delay(1000L) // ここでも有益なことをするふりをする
    return 29
}
```
これはこのようなものを出力する。
```
The answer is 42
Completed in 1017 ms
```
この通り、ここでは二つのコルーチンが定義されているが、以前の例のように実行はされない。しかし、[start](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/start.html)を呼び出すことにより、まさにいつ実行を開始するかをプログラマに制御が与えられている。最初に`one`を開始し、その後`two`を開始し、その後で個々のコルーチンが完了するのを待つ。
留意すべきは、もし個々のコルーチンについて、最初に[start](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-job/start.html)を呼ばずに`println`の中で[await](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html)を呼ぶだけの場合、これはシーケンシャルなふるまいにつながるだろう。というのは、[await](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-deferred/await.html)はコルーチンの実行を開始し、完了を待つため、lazyの意図したユースケースではない。`async(start = CoroutineStart.LAZY)`のユースケースは、suspend関数を含む値の計算をする場合における標準の`lazy`関数の置き換えである。
## asyncスタイルの関数
構造化された並列性から離れた[GlobalScope](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html)の参照を使って、[async](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html)コルーチンビルダーを使って非同期に`doSomethingUsefulOne`と`doSomethingUsefulTwo`を呼び出すasyncスタイルの関数を定義することができる。そのような関数を"...Async"サフィックスをつけて名づけ、それらは非同期に計算が開始され、誰かが結果を得るために生じるdeferredな値を使うことを必要とする、という事実を強調する。

::: note
[GlobalScope](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-global-scope/index.html)は、以下で説明するように、簡単でない方法に裏目に出ることがある、繊細なAPIである。そのため`GlobalScope`を使うには、`@OptIn(DelicateCoroutinesApi::class)`とともに明示的なopt-inが必要である。
:::

```kotlin
// somethingUsefulOneAsyncの結果の型はDeferred<Int>である
@OptIn(DelicateCoroutinesApi::class)
fun somethingUsefulOneAsync() = GlobalScope.async {
    doSomethingUsefulOne()
}

// somethingUsefulTwoAsyncの結果の型はDeferred<Int>である
@OptIn(DelicateCoroutinesApi::class)
fun somethingUsefulTwoAsync() = GlobalScope.async {
    doSomethingUsefulTwo()
}
```
留意すべきは、`xxxAsync`関数は、suspend関数ではない。それらはどこからでも使える。しかしそれらを呼び出すコードで実行するふるまいは、常に非同期(ここでは並列を意味する)であることを示す。
次の例で、それらをコルーチンの外で使うことをお見せする。
```Kotlin
import kotlinx.coroutines.*
import kotlin.system.*

// 留意すべきは、この例では`main`の右に`runBlocking`が無いということである
fun main() {
    val time = measureTimeMillis {
        // コルーチンの外でasync実行を開始することができる
        val one = somethingUsefulOneAsync()
        val two = somethingUsefulTwoAsync()
        // しかし、結果を待つにはsuspendingかblockingに含めなければならない。
        // ここでは、結果を待つ間、メインスレッドをブロックするため`runBlocking { ... }`を使用する
        runBlocking {
            println("The answer is ${one.await() + two.await()}")
        }
    }
    println("Completed in $time ms")
}

@OptIn(DelicateCoroutinesApi::class)
fun somethingUsefulOneAsync() = GlobalScope.async {
    doSomethingUsefulOne()
}

@OptIn(DelicateCoroutinesApi::class)
fun somethingUsefulTwoAsync() = GlobalScope.async {
    doSomethingUsefulTwo()
}

suspend fun doSomethingUsefulOne(): Int {
    delay(1000L) // ここで有益なことをするふりをする
    return 13
}

suspend fun doSomethingUsefulTwo(): Int {
    delay(1000L) // ここでも有益なことをするふりをする
    return 29
}
```
::: note
このasync関数のプログラミング形式は、例示のために提供されている。なぜならそれは他のプログラム言語では一般的な形式だからである。Kotlinコルーチンに関してこの形式は、以下で説明する理由により、**推奨しない**。
:::

`val one = somethingUsefulOneAsync()`の行と、`one.await()`式の間で、コード内に何かロジックエラーがあり、プログラムが例外をスローし、プログラムにより実行されていた操作が異常終了するとどうなるか、考えてみよう。通常は、グローバルなエラーハンドラーがその例外をキャッチし、開発者にエラーのログと報告をするが、プログラムはそれ以外は他の操作を継続することがありうる。しかしここで、たとえ開始した操作が異常終了したとしても、`somethingUsefulOneAsync`はまだバックグラウンドで実行している。この問題は、以下のセクションでお見せする構造化された並行性では発生しない。
## asyncを使う構造化された並行性
"asyncの使用による並列"の例を取り上げ、同時に`doSomethingUsefulOne`と`doSomethingUsefulTwo`を実行して、その結果の合計を返却する関数を取り出そう。[async](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/async.html)コルーチンビルダーは、[CoroutineScope](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html)の拡張として定義されているため、そのスコープ内で、それを持つ必要があり、それは[CoroutineScope](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines/-coroutine-scope/index.html)関数が提供するものである。
```Kotlin
suspend fun concurrentSum(): Int = coroutineScope {
    val one = async { doSomethingUsefulOne() }
    val two = async { doSomethingUsefulTwo() }
    one.await() + two.await()
}
```
この方法では、`concurrentSum`関数のコード内で何か変なことが起きる場合、例外をスローし、そのスコープ内で起動したすべてのコルーチンはキャンセルされるだろう。
```Kotlin
import kotlinx.coroutines.*
import kotlin.system.*

fun main() = runBlocking<Unit> {
    val time = measureTimeMillis {
        println("The answer is ${concurrentSum()}")
    }
    println("Completed in $time ms")    
}

suspend fun concurrentSum(): Int = coroutineScope {
    val one = async { doSomethingUsefulOne() }
    val two = async { doSomethingUsefulTwo() }
    one.await() + two.await()
}

suspend fun doSomethingUsefulOne(): Int {
    delay(1000L) // ここで有益なことをするふりをする
    return 13
}

suspend fun doSomethingUsefulTwo(): Int {
    delay(1000L) // ここでも有益なことをするふりをする
    return 29
}
```
上の`main`関数の出力から明らかなように、まだ並行に実行をしている。
```
The answer is 42
Completed in 1017 ms
```
キャンセルは、常にコルーチン階層を通じて伝播する。
```Kotlin
import kotlinx.coroutines.*

fun main() = runBlocking<Unit> {
    try {
        failedConcurrentSum()
    } catch(e: ArithmeticException) {
        println("Computation failed with ArithmeticException")
    }
}

suspend fun failedConcurrentSum(): Int = coroutineScope {
    val one = async<Int> { 
        try {
            delay(Long.MAX_VALUE) // 非常に長い計算をエミュレートする
            42
        } finally {
            println("First child was cancelled")
        }
    }
    val two = async<Int> { 
        println("Second child throws an exception")
        throw ArithmeticException()
    }
    one.await() + two.await()
}
```
最初の`async`と待っている親は、(`two`という名前の)子の一つの失敗で、両方ともキャンセルされることに注意してもらいたい。
```
Second child throws an exception
First child was cancelled
Computation failed with ArithmeticException
```