[Kotlin] coroutine 학습 - Appendix: 추가 학습

succeeding·2024년 7월 26일
0

Kotlin coroutine

목록 보기
3/3

suspendCoroutine

@SinceKotlin("1.3")
@InlineOnly
public suspend inline fun <T> suspendCoroutine(crossinline block: (Continuation<T>) -> Unit): T {
    contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
    return suspendCoroutineUninterceptedOrReturn { c: Continuation<T> ->
        val safe = SafeContinuation(c.intercepted())
        block(safe)
        safe.getOrThrow()
    }
}

suspendCoroutine 은 callback 을 실행한 후 suspendFunction 을 실행한 부분에서 해당 coroutine 을 중단한다.

suspend fun main() {
    println("Before")
    delay(1)
    suspendCoroutine<Unit> { continuation ->
        println("Before too")
    }

    println("After")
}

위 코드는

Before
Before too

까지만 출력되고 멈춘다.

이후 callback 에서 continuation.resume 을 호출했을 때, 멈춘 부분부터 coroutine 을 재개한다.

callback 을 suspend function 으로 바꾸기

참고 문서: Stack overflow - What is suspendCoroutine?

suspendCoroutine 을 이용하면 기존의 callback 으로 구현된 비동기 함수들을 suspend 형식으로 바꿀 수 있다.
마치 node.js 에서 Promise 로 구현된 callback 지옥을 async/awiat 으로 풀 수 있었던 것처럼 Kotiin 에서도 그런 게 suspendCoroutine 을 이용하면 가능해진다.

즉, 아래와 같은 코드가

Api.getUser(id) { user ->
      Api.getProfile(user) { profile ->
          Api.downloadImage(profile.imageId) { image ->
              // ...
          }
      } 
}

아래와 같이 flat 해질 수 있다.

val user = getUser(id)
val profile = getProfile(user)
val image = downloadImage(profile.imageId)
//...

이게 어떻게 가능해지는지 조금 더 들어가보면, 가령 아래와 같은 코드가 있다고 해보자.

fun getUser(id: String, callback: (User) -> Unit) {...}

여기서 suspendCoroutine 을 이용하여 suspend function 으로 다시 쓸 수 있다.

suspend fun getUser(id: String): User  = suspendCoroutine { continuation ->
      Api.getUser(id) { user ->
          continuation.resume(user)
      }
}

위 코드는 suspendCoroutine 의 lambda 블록을 실행하고 진행 중이던 coroutine 을 중단한다.
lambda 블록은 실행 중에 비동기 API 인 Api.getUser 를 호출한다.
getUser 의 응답이 오고 callback 함수가 실행된다.
이 callback 함수에서 continuation.reusme(user) 를 실행함으로서, 응답 결과인 user 를 가지고 중단되었던 coroutine 을 재개한다.

0개의 댓글