기본 빌더에서 예외가 자식에서 부모로 전파되는 것을 이해하고 어떻게 멈추는지 이해해보자! ⚡
Job을 사용할 경우, 자식의 예외가 부모를 멈추게 하여 전체 흐름이 종료될 수 있음.runBlocking, launch, async)에서 예외가 발생하면, 해당 예외는 부모 코루틴으로 전파되고, 부모가 취소되면 자식들도 함께 취소됨fun main(): Unit = runBlocking {
launch {
launch {
delay(1000)
throw Error("Some error")
}
launch {
delay(2000)
println("Will not be printed")
}
launch {
delay(500) // 예외 발생보다 빠름
println("Will be printed")
}
}
launch {
delay(2000)
println("Will not be printed")
}
}
// Will be printed
// Exception in thread "main" java.lang.Error: Some error...

try-catch문을 통해 래핑하는 건 도움이 되지 않음fun main(): Unit = runBlocking {
try { // 래핑해도 무시됨
launch {
delay(1000)
throw Error("Some error")
}
} catch (e: Throwable) { // 아무 도움도 되지 않음
println("Will not be printed")
}
launch {
delay(2000)
println("Will not be printed")
}
}
// Exception in thread "main" java.lang.Error: Some error...
SupervisorJob을 사용하면 자식에서 발생한 모든 예외를 무시할 수 있음

일반적으로
SupervisorJob은 다수의 코루틴을 시작하는 스코프로 사용됨
fun main(): Unit = runBlocking {
val scope = CoroutineScope(SupervisorJob())
scope.launch {
delay(1000)
throw Error("Some error")
}
scope.launch {
delay(2000)
println("Will be printed")
}
delay(3000)
}
// Exception ...
// Will be printed

‼️
SupervisorJob을 부모 코루틴의 인자로 사용하지 마십시오. 이 역시 아무런 도움이 되지 않습니다.
fun main(): Unit = runBlocking {
// 잘못된 예
// 자식 코루틴 하나가 있고, 부모 코루틴이 없는 잡은 일반 잡과 동일하게 작동
launch(SupervisorJob()) {
launch {
delay(1000)
throw Error("Some error")
}
}
launch {
delay(2000)
println("Will not be printed")
}
delay(3000)
}
// Exception ...
fun main(): Unit = runBlocking {
val job = SupervisorJob()
launch(job) {
delay(1000)
throw Error("Some error")
}
launch(job) {
delay(2000)
println("Will be printed")
}
job.join()
}
// (1초 후)
// Exception ...
// (1초 후)
// Will be printed
supervisorScope로 래핑하는 방법이 있음fun main(): Unit = runBlocking {
supervisorScope{
launch(job) {
delay(1000)
throw Error("Some error")
}
launch(job) {
delay(2000)
println("Will be printed")
}
}
delay(1000)
println("Done")
}
// Exception ...
// Will be printed
// (1초 후)
// Done
supervisorScope는 중단 함수 본체를 래핑하는데 사용됨suspend fun notifyAnalytics(actions: List<UserAction>) =
supervisorscope {
actions.forEach { action ->
launch {
notifyAnalytics(action)
}
}
}
supervisorScope는 withContext(SupervisorJob())으로 대체될 수 없음// 이렇게 하면 안 됩니다!
suspend fun sendNotifications(
notifications: List<Notification>
) = withContext(SupervisorJob()) {
for (notification in notifications) {
launch {
client.send(notification)
}
}
}

suspend fun main() = supervisorScope {
val str1 = async<String> {
delay(1000)
throw MyException()
}
val str2 = async {
delay(2000)
"Text2"
}
try {
println(strl.await())
} catch (e: MyException) {
println(e)
}
println(str2.await())
}
// MyException
// Text2
supervisorScope가 사용되어 또 다른 async는 중단되지 않고 끝까지 실행됨CancellationException의 서브클래스라면 부모로 전파되지 않고 현재 코루틴을 취소시킴object MyNonPropagatingException : CancellationException()
suspend fun main(): Unit = coroutineScope {
launch { // 1
launch { // 2
delay(2000)
println("Will not be printed")
}
throw MyNonPropagatingException // 3
}
launch { // 4
delay(2000)
print("Will be printed")
}
}
// (2초 후》
// Will be printed
CancellationException의 서브타입인 MyNonPropagationException 예외를 던짐launch에서 잡힘launch는 영향을 받지 않고 2초 후에 “Will be printed”를 출력함CoroutineExceptionHandler 컨텍스트를 사용하면 편리fun main(): Unit = runBlocking {
val handler =
CoroutineExceptionHandler { ctx, exception ->
println("Caught sexception")
}
val scope = CoroutineScope(SupervisorJob() + handler)
scope.launch {
delay(1000)
throw Error("Some error")
}
scope.launch {
delay(2000)
println("Will be printed")
}
delay(3000)
}
// Caught java.lang.Error: Some error
// Will be printed