Kotlin Coroutinesのasync/awaitの挙動を確認してみる

RxjavaのコードをCoroutinesで書き直せるのか?など調べていくうちにCoroutinesでの動き方に曖昧に理解しているところがあったので動かしてみた結果をメモしておく。

  • Kotlin version : 1.3.31
  • Coroutines version: 1.2.1

とても参考にしました :bow: satoshun.github.io

private suspend fun completeFirst(): String {

        delay(2000)

        Timber.d("completeFirst ")

        return "completeFirst"

}

private suspend fun completeSecond(): String {

        delay(1000)

        Timber.d("completeSecond")

        return "completeSecond"

}

前提としてこういったsuspend関数があるとする。

素直に実行する

uiScope.launch {

completeFirst()

completeSecond()

}

Output: completeFirstが実行された後にcompleteSecondが実行される

D/MainActivity: completeFirst 

D/MainActivity: completeSecond

asyncをつける

uiScope.launch {

async{ completeFirst() }

async{ completeSecond() }

}

Output: pararellに実行されるのでcompleteSecondの方が先にlogが出る

D/MainActivity: completeSecond
D/MainActivity: completeFirst 

Exceptionをthrowするとき

suspend関数内でExceptionを吐くときの挙動を確認する。

private suspend fun completeFirst(): String {

        delay(2000)

        Timber.d("completeFirst ")

        throw Throwable("test first")

        return "completeFirst"

}

private suspend fun completeSecond(): String {

        delay(1000)

        Timber.d("completeSecond")

        return "completeSecond"

}

uiScope.launch {

completeFirst()

completeSecond()

}

Output: completeSecondは実行されず、completeFirstのthrowでアプリはクラッシュする

D/MainActivity: completeFirst

E/AndroidRuntime: FATAL EXCEPTION: main

java.lang.Throwable: test first 

asyncをつける

completeSecondでも例外を吐くときを考える

private suspend fun completeFirst(): String {

        delay(2000)

        Timber.d("completeFirst ")

        return "completeFirst"

}

private suspend fun completeSecond(): String {

        delay(1000)

        Timber.d("completeSecond")

        throw Throwable("test first")

        return "completeSecond"

}
uiScope.launch {

async{ completeFirst() }

async{ completeSecond() }

}

Output: asyncをつけても例外をthrowされた時点でアプリはcrashする

D/MainActivity: completeSecond

E/AndroidRuntime: FATAL EXCEPTION: main

java.lang.Throwable: test first 

try-catchをつける

uiScope.launch {

try{

async{ completeFirst() }

async{ completeSecond() }

} catch (th:Throwable) {

Timber.e(th)

}

}

Output: crashする。async内で起こったcrashはcatchできない。

asyncの中でtry-catch/runCatching

uiScope.launch {

async{ runCatching{completeFirst()} }

async{ runCatching{completeSecond()} 

}

Output:これでアプリはcrashしなくなる。

async{runCatching{completeFirst()}} の返り値はResult型で、isFailure isSuccessで結果がわかるのでこれを組み合わせればRxJavaでいうzipとかcompleteDelaymerge のような動きもかけそう。