コルーチンとチャネル入門#4 suspend関数の使用

ソース記事は[こちら](https://play.kotlinlang.org/hands-on/Introduction%20to%20Coroutines%20and%20Channels/04_Suspend)
Retrofitにはコルーチンに対するネイティブなサポートが提供されているため、それを使うことにする。`Call<List<Repo>>`を返却する代わりに、今では`suspend`関数としてAPI呼び出しを定義する。
```Kotlin
interface GitHubService {
    @GET("orgs/{org}/repos?per_page=100")
    suspend fun getOrgRepos(
        @Path("org") org: String
    ): List<Repo>
}
```
その裏にある主な考えは、要求を実行するために`suspend`関数を使うときは、配下のスレッドがブロッキングされないことである。まさにそれがどのように動くか、少し後で説明する。
留意すべきは、今では`getOrgRepos`は、`Call`を返却する代わりに、直接結果を返却する。もし結果が成功でない場合は、例外がスローされる。
あるいは、Retrofitは`Response`内に結果をラップして返却することもできる。このケースでは、結果本体は提供され、手動でエラーをチェックすることができる。このチュートリアルでは`Reponse`バージョンを使用する。
`GitHubService`にどうか次の宣言を追加してもらいたい。
```Kotlin
interface GitHubService {
    // getOrgReposCall と getRepoContributorsCall 宣言 ※以下のメソッドを追加する
    
    @GET("orgs/{org}/repos?per_page=100")
    suspend fun getOrgRepos(
        @Path("org") org: String
    ): Response<List<Repo>>

    @GET("repos/{owner}/{repo}/contributors?per_page=100")
    suspend fun getRepoContributors(
        @Path("owner") owner: String,
        @Path("repo") repo: String
    ): Response<List<User>>
}
```
課題は、コントリビューターをロードする関数のコードを変更し、これらの新しい`suspend`関数を利用することである。
`suspend`関数はどこからも呼び出すことはできない。もし、`loadContributorsBlocking`から呼び出すと、「suspend関数'getOrgRepos'は、コルーチンまたは他のsuspend関数からのみ呼び出す必要があります」というエラーになる。そのため、この新しいAPIを使うためには、`loadContributors`関数の新しいバージョンに`suspend`の印をつける必要がある。
それでは次のタスクを行っていただき、その後で、`suspend`関数がどのように動作するか、通常の関数とどう異なるのかを説明する。`suspend`関数をsuspendでない関数から呼び出す方法についても見ていく。
## 課題
`loadContributorsBlocking`の実装(`src/tasks/Request1Blocking.kt`に定義されている)を、`loadContributorsSuspend`(`src/tasks/Request4Suspend.kt`)にコピーすること。その後、`Call`を返却する関数を新しく`suspend`関数を使う方法で修正すること。プログラムを`SUSPEND`オプションを選んで実行し、UIがGitHub要求を実行している間でも、応答することを確認する。
## 解法
コードの修正は簡単である。単に`.getOrgReposCall(req.org).execute()`を`.getOrgRepos(req.org)`に置き換え、同じ置換を二つ目の「コントリビューター」要求にも繰り返す、それだけである！その他のすべては同じである。※`.execute()`行は削除する。
```Kotlin
suspend fun loadContributorsSuspend(service: GitHubService, req: RequestData): List<User> {
    val repos = service
        .getOrgRepos(req.org)
        .also { logRepos(req, it) }
        .bodyList()

    return repos.flatMap { repo ->
        service.getRepoContributors(req.org, repo.name)
            .also { logUsers(repo, it) }
            .bodyList()
    }.aggregate()
}
```
留意すべきは、`loadContributorsSuspend`は`suspend`関数として定義されなければならない。
以前`Response`を返却した`execute`を呼ぶ必要はもはやなく、なぜなら今ではAPI関数は直接`response`を返却するためである。しかしそれはRetrofitライブラリ固有の実装の詳細である。他のライブラリでは、APIは異なるだろうが、考えは同じである。
`suspend`関数でのコードは「ブロッキング」版と驚くほど似ている。それは読みやすく、成し遂げようとしていることを正確に表している。
ブロッキング版との主な違いは、スレッドをブロックする代わりに、コルーチンをsuspendする。
```
block -> suspend
thread -> coroutine
```
コルーチンは度々軽量のスレッドと呼ばれる。これはスレッドでコードを実行する方法と同じようにコルーチンでコードを実行できるという意味である。以前ブロッキングしていた演算(そのために避けなければならなかった)は、今では代わりにコルーチンをsuspendする。
どのようにコルーチンを開始することができるか？`loadContributorsSuspend`を呼ぶ方法を見ると、`launch`の内側で呼び出していることがわかる。`launch`はラムダを引数にとるライブラリ関数である。
```Kotlin
launch {
    val users = loadContributorsSuspend(req)
    updateResults(users, startTime)
}
```
`launch`は新しい計算を開始する。この計算はデータのロード処理と結果の表示を担っている。この計算は一時停止可能である。つまりネットワーク要求を行っている間、「一時停止」状態になり、配下のスレッドを解放する。ネットワーク要求が結果を応答すると、計算は復帰する。
そのような一時停止可能な計算はコルーチンと呼ばれ、このケースで簡単に言うと、`launch`はデータのロードと結果の表示を担う新しいコルーチンを開始する。
コルーチンはスレッドの先頭で実行し、一時停止できる計算である。コルーチンが「一時停止」状態になると、対応する計算は停止され、スレッドから取り除かれ、メモリに格納される。その間、スレッドは他の活動で埋めるために自由である。
![gif](https://play.kotlinlang.org/resources/hands-on/Introduction%20to%20Coroutines%20and%20Channels/assets/4-suspend/SuspensionProcess.gif)
計算が継続の準備ができたら、スレッドに復帰する(ただし、必ずしも同じスレッドとは限らない)
`loadContributorsSuspend`の例に戻ってみよう。それぞれの「コントリビューター」要求は、今では一時停止メカニズム経由で結果を待つ。最初に新しい要求を送信する。その後、結果を待つ間、全体の「コントリビューターのロード」コルーチンは一時停止状態になる(`launch`関数によって開始された、先に説明したものである)。コルーチンは、対応する応答を受け取った後でのみ、復帰する。
![gif2](https://play.kotlinlang.org/resources/hands-on/Introduction%20to%20Coroutines%20and%20Channels/assets/4-suspend/SuspendRequests.png)
応答を受け取るのを待つ間、スレッドは他のタスクで埋めるために自由である。`COROUTINE`オプション経由でユーザーをロードするときに、すべての要求がメインのUIスレッドで発生するにも関わらず、Uiが反応できる状態であるのはそのためである。
```
2538 [AWT-EventQueue-0 @coroutine#1] INFO Contributors - kotlin: loaded 30 repos
2729 [AWT-EventQueue-0 @coroutine#1] INFO Contributors - ts2kt: loaded 11 contributors
3029 [AWT-EventQueue-0 @coroutine#1] INFO Contributors - kotlin-koans: loaded 45 contributors
...
11252 [AWT-EventQueue-0 @coroutine#1] INFO Contributors - kotlin-coroutines-workshop: loaded 1 contributors
```
どのコルーチンが対応するコードで実行されているか、ログで見ることができる。それを可能にするには、`実行-実行構成の編集...`を開き、`-Dkotlinx.coroutines.debug`VMオプションを追加する。
![vm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627223/bdfb2623-5085-b248-7fd1-9bfc81039d0f.png)

その後、このオプションとともに`main`を実行する間、コルーチンの名前はスレッド名に接続される。すべてのKotlinファイルを実行するためのテンプレートを変更することで、デフォルトでこのオプションを有効にすることもできる。
今回のケースではすべてのコードは`@coroutine#1`として表示される上記の「コントリビューターのロード」コルーチンという、一つのコルーチンで実行される。
このバージョンでは、結果を待つ間、他の要求を送るために、スレッドを再利用しない。なぜならコードがシーケンシャルに書かれているためである。新しい要求は、前の結果を受けたときのみ、送られる。`suspend`関数はスレッドを公平に扱い、「待つ」ためにブロックはしないが、並列性はまだ実現していない。これを改善する方法を見ていこう。


https://qiita.com/CuriosityKilledTheCat/items/10c372b5f1405007b9b7