Open lee-ji-hoon opened 2 months ago
책을 읽다가
coroutineScope
를 사용하면 자식 코루틴에서 발생한 예외는coroutineScope
가 생성한 코루틴을 중단하게 되어 모든 자식 코루틴을 취소한 뒤 예외를 던지게 만든다.
위 내용이 있어서 2주 전에 디스코드에 올렸던 의문점이 있는 코드에서 뭔가 정답을 찾을 수 있을거 같아 다시 디버깅 해보았다.
suspend fun getData(): Int {
delay(1.seconds)
throw Exception("tawet")
return 10
}
suspend fun main() = coroutineScope {
val result = runCatching {
val test = async { getData() }
test.await()
}
result.onSuccess {
println("onSuccess: $it")
}.onFailure { e ->
println("onFailure: $e")
}
delay(3.seconds)
println("Process completed")
}
실무에서 이런 비슷한 코드가 viewModelScope + runCatching인데 터지는 문제가 생겼다.
viewModelScope
에서 withContext
가 아닌 async
로 열었으니 SupervisorJob
은 자식이 못받는 것은 이전에 봤었다.exception
이 부모로 전파가 되지만 runCatching
이니까 onFailure
에서 잡혀야 한다.이렇게 생각의 흐름이 흘렀는데 실제로 2번 까지는 맞았는데 하나가 더 있었다.
onFailure
부분이 동작을 하고 Exception이 또 터져서 앱이 죽어버렸다.그래서 이거에 대해서 조금 더 살펴보고자 한다.
우선 이런형태로 Exception이 나고 Process 자체가 죽어버리는데, 내가 지금 의심이 되는건 Exception
이 전파가 되고 async
블럭 안에서 Exception이 또 터져서 비동기적으로 Exception이 나면서 runCatching이 동작을 안하는게 아닐까? 라는 생각이 들었다.
suspend fun getData(): Int {
delay(5.seconds)
throw Exception("tawet")
return 10
}
suspend fun main() = coroutineScope {
val result = runCatching {
launch { getData() }
}
result.onSuccess {
println("onSuccess: $it")
}.onFailure { e ->
println("onFailure: $e")
}
delay(3.seconds)
println("Process completed")
}
그래서 이런 형태로 바꾸고 테스트를 해보았다.
async
> launch
getData
의 delay
를 "Process completed"
보다 더 늦게 끝나게 delay를 5초로 변경이렇게 하자 신기한 결과가 나왔다.
이런 결과가 나와서 그러면 이제는 runCatching
보다 늦게 끝나서 그런건가 하고 getData()
의 delay
를 없애고 실행하자 맨 처음 테스트 처럼 그냥 프로세스 자체가 죽어버렸다.
지금 보면 coroutineScope
의 자식으로 launch
혹은 async
가 있는 형태인데 exception
은 전파가 되기 때문에 coroutineScope
의 block의 exception이 coroutineScope
자체까지 전파가 되기 때문이 아닐까? 라는 생각하고 밖에 try catch를 해보았다.
suspend fun main() {
try {
coroutineScope {
val result = runCatching {
launch { getData() }
}
delay(1.seconds)
result.onSuccess {
println("onSuccess: $it")
}.onFailure { e ->
println("onFailure: ${e.stackTrace.iterator().forEachRemaining { println(it)}}")
}
delay(3.seconds)
println("Process completed")
}
} catch (e: Exception) {
println("======================================")
println("catch e > ${e.stackTrace.iterator().forEachRemaining { println(it) }}")
}
}
그러면 이런 결과를 받을 수 있는데 catch는 되고 프로세스 자체가 죽지는 않는 내가 원하는 방향으로 잡혔다. 그래서 supervisorJob이 아니거나 supervisorScope으로 열지 않았다면 최상위 부모까지 error가 전파가 되고 부모 Coroutine 자체에서 Exception이 터지기 때문에 내부 block에 runCatching을 잡아도 의미가 없다.
26, 27장