Kotlin【入門-Coroutine①-】

# 

## 概要
kotlin coroutineについてキャッチアップしてみました。

参考資料：
[Coroutine Basics](https://kotlinlang.org/docs/reference/coroutines/basics.html)
[Cancellation and Timeouts](https://kotlinlang.org/docs/reference/coroutines/cancellation-and-timeouts.html)
[Composing Suspending Functions](https://kotlinlang.org/docs/reference/coroutines/composing-suspending-functions.html)

### 基本系
1. `GlobalScope.launch`でコルーチンを起動(1秒停止)
2. println("Hello,")
3. メインスレッドで2秒停止
4. 1.の1秒停止が終わり、println("World!")
5. 3.の2秒停止が終了
5. アプリケーションが終了

```main.kt
import kotlinx.coroutines.*

fun main() {
  GlobalScope.launch {
      delay(1000L) // コルーチンで1秒停止
      println("World!")
  }
  println("Hello,")
  Thread.sleep(2000L) // メインスレッドで2秒停止
}
```

#### ポイント
- コルーチンは`GlobalScope.launch`で起動する
- アプリケーションが終了すれば動作しない

### Blocking(ブロッキング)

```main.kt
fun main() {
    GlobalScope.launch {
        delay(1000L) // コルーチンで1秒停止 non-blocking
        println("World!")
    }
    println("Hello,")
    runBlocking {
        delay(2000L) // メインスレッドを2秒停止
    }
}

// 以下のようにすることでも同じ結果を得られる
fun main() = runBlocking {
    GlobalScope.launch {
        delay(1000L) // コルーチンで1秒停止 non-blocking
        println("World!")
    }
    println("Hello,")
    delay(2000L)
}

// runBlockingを用いることでコルーチンが終了するまで、アプリケーションの終了を待つことができる
fun main() = runBlocking {
    val job = GlobalScope.launch {
        delay(1000L)
        println("World!")
    }
    println("Hello,")
    job.join()
}

// `GlobalScope.launch`ではなく`launch`を用いることでこのスコープ内でコルーチンを起動する
fun main() = runBlocking {
    launch {
        delay(1000L)
        println("World!")
    }
    println("Hello,")
}
```

#### ポイント
- `runBlocking {...}`を使用すると内部のコルーチンが終了するまでスレッドをブロック（停止）できる
- `GlobalScope.launch`と`launch`で起動するスコープを指定できる

### coroutineScope（スコープ）
```main.kt
fun main() = runBlocking {
    // 処理が完了するまでスレッドを停止する
    launch {
        delay(200L)
        println("Task from runBlocking")
    }

    coroutineScope {
        launch {
            delay(500L)
            println("Task from nested launch")
        }

        delay(100L)
        println("Task from coroutine scope")
    }

    println("Coroutine scope is over")
}
```

#### ポイント
- runBlockingはブロック（停止）、coroutineScopeは一時停止
- `GlobalScope.launch`と`launch`で起動するスコープを指定できる

### Cancel（キャンセル）
```main.kt
fun main() = runBlocking {
    val job = launch {
        repeat(1000) { i ->
            println("job: I'm sleeping $i ...")
            delay(500L)
        }
    }
    delay(1300L)
    println("main: I'm tired of waiting!")
    job.cancel() // jobのキャンセル
    job.join() // job完了を待つ
    println("main: Now I can quit.")
}

fun main() = runBlocking {
    val job = launch {
      try {
        repeat(1000) { i ->
            println("job: I'm sleeping $i ...")
            delay(500L)
        }
      } finally {
        // 以下のコルーチンはキャンセルされないようにする
        withContext(NonCancellable) {
          println("job: I'm running finally")
          delay(1000L)
          println("job: And I've just delayed for 1 sec because I'm non-cancellable")
        }
      }
    }
    delay(1300L)
    println("main: I'm tired of waiting!")
    job.cancelAndJoin() // jobのキャンセル&完了を待つ
    println("main: Now I can quit.")
}

```

#### ポイント
- `cancel`メソッドでjob(コルーチン)をキャンセルできる
- `join`でjob(コルーチン)の終了まで待つ
- `cancelAndJoin`というcancelとjoinが合わさったメソッドもある
- `try {...} finally {...}`を用いることでキャンセルされた際に`finally`ブロックを通過する
- `withContext(NonCancellable) {...}`を用いることでキャンセルされないようにできる

### Timeout（タイムアウト）
```main.kt
fun main() = runBlocking {
    withTimeout(1300L) {
        repeat(1000) { i ->
            println("I'm sleeping $i ...")
            delay(500L)
        }
    }
}

fun main() = runBlocking {
    val result = withTimeoutOrNull(1300L) {
    repeat(1000) { i ->
        println("I'm sleeping $i ...")
        delay(500L)
    }
    "Done" // will get cancelled before it produces this result
  }
  println("Result is $result")
}
```

#### ポイント
- `withTimeout`でjob(コルーチン)の制限時間を設定できます(タイムアウト時は例外をスローする)
- `withTimeoutOrNull`ではタイムアウト時にnullを返す


### Suspend関数
```main.kt
fun main() = runBlocking {
    launch { doWorld() }
    println("Hello,")
}

// this is your first suspending function
suspend fun doWorld() {
    delay(1000L)
    println("World!")
}
```

```main.kt
suspend fun doSomethingUsefulOne(): Int {
    delay(1000L)
    return 13
}

suspend fun doSomethingUsefulTwo(): Int {
    delay(1000L)
    return 29
}

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

// The answer is 42
// Completed in 1017 ms
fun main() = runBlocking {
    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修飾子を用いることでコルーチン内で使用できる`delay`関数などを呼び出せる
- suspend関数はコルーチンスコープないでしか呼び出せない
- async/awaitキーワードを使用することによって非同期処理にできる

## 最後に
アルサーガパートナーズ Advent Calendar 2020 22日目は @miumiさんのターンです。:tada: