withContext
코루틴을 만드는 코루틴 빌더로, async
는 await()
을 호출해야 결과값을 받지만 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
/* ... */
}