3장.라이프 사이클과 에러 핸들링

Job과 Deferred

  • 결과가 없는 비동기 함수: Job vs 결과가 있는 비동기 함수: Deferred

Job

  • 파이어 앤 포겟(fire-and-forget) 작업
  • 특정 상태에 도달하면 이전 상태로 되돌아가지 않는다.
  • 코루틴 빌더 launch{} 를 이용
  • 예외는 Job을 생성한 곳까지 전파시킨다.
//job은 예외를 던진 곳까지 전파시킨다.
fun main(args : Array<String>) = runBlocking{
    val job = GlobalScope.launch {
        TODO("Not Implemented")
    }

    delay(2000L)   
}

생명주기

생성(New)

  • 존재하지만 아직 실행되지 않은 Job
  • CoroutineStart.LAZY를 통해 Job을 자동으로 시작시키지 않을 수 있다.
fun main(args : Array<String>) = runBlocking{
    val job = GlobalScope.launch(start = CoroutineStart.LAZY) {
        TODO("Not Implemented")
    }
    delay(2000L)
}

활성(Active)

  • 실행중인 Job, 일시 중단된 Job도 포함
  • join() vs start()
    • join(): Job의 완료를 기다림
    • start(): Job의 완료를 기다리지 않음
fun main(args : Array<String>) : Unit = runBlocking{
    val job = GlobalScope.launch(start = CoroutineStart.LAZY) {
        delay(2000L)
        println("Complete")
    }
    job.join()
}

join()는 Job의 완료를 기다리기 때문에 일시 중단 함수 or 코루틴에서 호출해야 한다.

fun main(args : Array<String>) {
    val task = GlobalScope.launch {
        delay(1000L)
        println("Complete")
    }

    task.start()
}

Complete 출력 없이 프로그램이 종료되는 것을 확인할 수 있다.

취소 중(Canceling)

  • 활성과 취소의 중간 단계
  • 실행 중인 Job에서 cancel() 호출하여 취소가 완료 될 때 까지

취소 됨(Cancelled)

  • 취소로 완료된 Job
  • 취소와 완료는 같은 상태이므로 CoroutineException Handler를 이용하여 판단
//handler 처리 
fun main(args : Array<String>)  = runBlocking{
    val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
        println("Job cancelled due to ${throwable.message}")
    }


    GlobalScope.launch(exceptionHandler) {
        TODO("Not implemented yet")
    }

    delay(2000L)
}
//invokeOnCompleteion 처리
fun main(args : Array<String>)  = runBlocking{

    GlobalScope.launch {
        TODO("Not implemented yet")
    }.invokeOnCompletion { cause ->
        cause?.run {
            println("Job Cancelled due to $this")
        }
    }

    delay(2000L)
}

완료 됨(Completed)

  • Job이 더 이상 실행 x, 취소된 Job도 포함

Deferred

  • 결과를 갖는 비동기 작업에 수행하기 위한 목적
  • 객체를 반환, 객체는 비동기 작업이 완료될 때까지 비어있다.
  • 처리되지 않은 예외를 자동으로 전파하지 않는다.
  • join()은 에러를 전파하지 않고 처리하는 반면 await()는 단지 호출하는 것만으로 예외가 전파된다.
//try-catch 문으로 예외 처리가 가능하다.
fun main(args : Array<String>) : Unit  = runBlocking{
    val deferred = GlobalScope.async {
        TODO("Not implemented yet")
    }

    try {
        deferred.await()
    }catch (e : Throwable){
        println("Deferred cancelled due to ${e.message}")
    }
}
  • Job과 동일한 방법으로 CoroutineExceptionHandler로 처리할 경우, MainThread에 에러가 전파되어 MainThread가 에러로 인해 종료된다.
    따라서, CoroutineExceptionHandler를 이용하기 위해선 에러를 전파받는 위치에 Handler를 추가하는 것이다.
//에러를 전파 받는 곳에 handler를 이용한다.
fun main(args: Array<String>): Unit = runBlocking {
    val exceptionHandler = CoroutineExceptionHandler{_, handler ->
        println("Defered cancelled due to ${handler.message}")
    }

    val deferred = GlobalScope.async {
        TODO("Not implemented yet")
    }

    GlobalScope.launch(exceptionHandler){
        deferred.await()
    }.join()
}

상태는 한 방향으로 이동

  • Job이 특정 상태에 도달하면 이전 상태로 되돌아가지 않는다.
fun main(args: Array<String>): Unit = runBlocking {
    val task = measureTimeMillis {
        val job = GlobalScope.launch{
            delay(2000L)
        }

        job.join()

        //restart
        job.start()
        job.join()
    }

    println("Time: $task ms")
}

2초 정도의 시간이 출력되는 것을 확인할 수 있다

참고

좋은 웹페이지 즐겨찾기