withContext

코루틴을 만드는 코루틴 빌더 (CoroutineBuilder)

withContext

withContext


코루틴을 만드는 코루틴 빌더로, asyncawait() 을 호출해야 결과값을 받지만 withContext 는 처음부터 결과값을 반환할 때까지 대기한다.

  • 내부 코루틴 블록이 끝나고 결과값이 반환될 때까지 종료하지 않기에 try-finally 블록에서 자원 반납 등 코루틴을 마무리하는데 주로 사용한다.
  • 코루틴 취소 시 CancellationException 발생시키고 try-finally 블록에서 예외 처리를 한 경우 코루틴이 취소된 상황이기에 다른 중단 함수 등을 호출할 수 없으므로 withContext + NonCancellable 을 사용해야 한다.
fun main() = runBlocking {
    val job = launch {
        try {
            repeat(1000) {
                println("Sleeping... $it")
                delay(1000)
            }
        } finally {
            withContext(NonCancellable) {
                delay(1000)
                println("Bye!!")
            }
        }
    }

    delay(2000)
    println("Start")
    job.cancelAndJoin() // CancellationException 발생
    println("End")
}


// Sleeping... 0
// Sleeping... 1
// Start
// Bye!!
// End



withContext(NonCancellable)


withContext(NonCancellable) 사용 시 코루틴이 죽지 않고 끝나는 이유는 isActive() 가 항상 true 를 반환하도록 구현이 되어있기 때문에 코루틴이 Completed(종료)될 때 까지 무조건 true 를 반환해서 취소 예외에 영향 없이 작업을 수행하기 때문이다.

@Suppress("DeprecatedCallableAddReplaceWith")
public object NonCancellable : AbstractCoroutineContextElement(Job), Job {

    private const val message = "NonCancellable can be used only as an argument for 'withContext', direct usages of its API are prohibited"

    /**
     * Always returns `true`.
     * @suppress **This an internal API and should not be used from general code.**
     */
    @Deprecated(level = DeprecationLevel.WARNING, message = message)
    override val isActive: Boolean
        get() = true

    /**
     * Always returns `false`.
     * @suppress **This an internal API and should not be used from general code.**
     */
    @Deprecated(level = DeprecationLevel.WARNING, message = message)
    override val isCompleted: Boolean get() = false

    /**
     * Always returns `false`.
     * @suppress **This an internal API and should not be used from general code.**
     */
    @Deprecated(level = DeprecationLevel.WARNING, message = message)
    override val isCancelled: Boolean get() = false

    /* ... */
}
essential