suspendCancellableCoroutine

홍승범·2023년 1월 18일
0

Kotlin

목록 보기
2/8

코루틴은 콜백을 사용하지 않고 비동기 처리를 해 주는 장점을 가지고 있음

  • 이 때문에 비동기 처리를 코드 순서대로 실행시켜 가독성을 높이고 보다 심플한 코드를 작성하게 해준다.
  • 기존 라이브러리들이 비동기 처리를 콜백으로 지원하는 경우가 많다. 이럴경우 코루틴으로 처리하기가 애매해진다.
  • 이럴때 사용 가능한 것이 suspendCancellableCoroutine 이나 callbackFlow가 있다. 여기서는 suspendCancellableCoroutine에 대해 알아보고자 한다.

suspendCancellableCoroutine

suspendCancellableCoroutine의 정의는 아래 코드와 같다

inline suspend fun <T> suspendCancellableCoroutine(
                    crossinline block: (CancellableContinuation<T>) -> Unit): T

람다의 파라미터가 CancellableContinuation이며 취소와 resume이 가능한 객체다

일단 어떻게 사용하는지 코드를 보자.

  1. 기존 콜백방식의 코드는 아래와 같다.
  class TestViewModel : ViewModel() {
    ...

    // 네트워크 요청시 받는 Callback 형태
    interface NetworkResult {
        fun success(resultCode: Int)
        fun fail(cause: Throwable)
    }

    private var networkJob: Job? = null
    
    // 네트워크 접속 요청시 결과를 callback으로 넘겨주는 함수 (Dummy)
    private fun requestNetwork(resultCallback: NetworkResult) {
        networkJob = viewModelScope.launch {
            delay(500) //임의로 5초를 대기후 결과를 반환해 준다.
            resultCallback.success(200)
        }
    }
    ...
} 

위 코드는 1초 이내에 결과가 나오길 기대하는 기도메타 같은 코드이다. 모든 상황에서 1초이내에 결과가 나올것인가? 아무리봐도 아니다.

이런 경우에 콜백 처리를 할 수 있도록 코루틴을 지연시킬 수 있는 처리를 suspendCancellableCoroutine이 지원해 준다. 아래 코드와 같다

suspend fun suspendCoroutineTest(): Int {
    val id = 155
    val result = suspendCancellableCoroutine<Int> { continuation ->
        TMDBService.service.getMovieDetail(id.toString()).enqueue(object : Callback<ResponseMovie> {
            override fun onResponse(call: Call<ResponseMovie>, response: Response<ResponseMovie>) {
                response.let {
                    if (it.isSuccessful) {
                            continuation.resume(2)
                    }
                }
            }

            override fun onFailure(call: Call<ResponseMovie>, t: Throwable) {
            }
        })
    }
    return result
}

suspendCancellableCoroutine은 블록내의 코드를 모두 수행한 후 해당 블록을 blocking 상태로 둔다. 그리고 resume / resumeWithException이 호출 되고 나서야 그 값을 블록 밖으로 전달하고, 재개된다.

  • resume은 코드를 재개하고 결과를 전달.
  • resumeWithException은 발생한 예외를 외부로 throw해주고, 코드를 재개한다.

suspendCancellableCoroutine은 cancel이 가능하기 때문에(suspendCoroutine 도 있는데 이것은 cancel이 불가능하고, 나머지는 동일함) 취소에 대한 처리가 필요하다.

취소 처리는 두가지 방법이 있다.
1. invokeOnCancellation 블록을 선언해 주거나
2. resume의 람다로 cancel시의 액션을 정의해 주는 방법

continuation.invokeOnCancellation {
    // 취소시 해야할 처리를 정의한다.
}

해당 취소 블록들은 job이 취소될 때에만 동작하며, 성공적으로 resume되면 동작하지 않는다. 또한 resume의 람다와 invokeOnCancellation 둘다 선언해 놓은경우 cancel되면 둘다 동작하게 된다.


https://tourspace.tistory.com/442

profile
그냥 사람

0개의 댓글