suspend func findUser
가 suspend func findProfile
, suspend func findImage
를 순서대로 호출할 때findUser()
, findUSer가 findProfile()
를 호출할 때 Continuation을 전달함findProfile()
이 가지고 있던 Continuation 에서 resumeWith()
를 콜백 함수처럼 실행하여 findUser()
를 호출함findUser()
는 findProfile()
의 결과와 Continuation 을 함께 돌려받고, 다시 findImage()
를 동일한 방식으로 호출함graph TD A[최초 진입] -->|1| findUser findUser -->|2| findProfile findProfile -->|3| Continuation1["Continuation label=0"] Continuation1 -->|"4 resumeWith()"| findUser findUser -->|5| findImage findImage -->|6| Continuation2["Continuation label=1"] Continuation2 -->|"7 resumeWith()"| findUser
두 개의 suspend 함수를 호출
findUser
함수가findProfile
과findImage
함수를 호출합니다.Continuation을 전달하여 Callback으로 활용
- 초기 진입은
findUser
에서 시작됩니다. (1)findUser
가findProfile
을 호출할 때, Continuation 객체를 전달합니다. (2)findProfile
이 종료되면 Continuation 객체의resumeWith
메서드를 호출하여findUser
로 돌아갑니다. (4)findUser
는findProfile
의 결과와 함께 Continuation을 받아 처리합니다.- 이후
findImage
를 동일한 방식으로 호출합니다. (5)findImage
가 종료되면, 동일하게 Continuation 객체의resumeWith
메서드를 호출하여findUser
로 돌아갑니다. (7)
Continuation
이라는 것을 전달하고, Label
을 매겨서 콜백 패턴으로 함수를 연결한다.Continuation과 resumeWith
코틀린의 코루틴에서는
Continuation
인터페이스와resumeWith
메서드를 사용하여 코루틴을 중단하고 재개할 수 있습니다. 이는 상태 머신과 유사한 방식으로 동작하며, 비동기 작업을 처리하는 데 매우 유용합니다.Continuation
Continuation
인터페이스는 코루틴의 중단된 상태를 저장하고, 나중에 해당 상태를 복원하여 실행을 재개할 수 있게 합니다. 이는 코루틴이 중단 지점에서 상태를 저장하고, 이후에 다시 재개할 때 그 상태에서 계속 진행할 수 있도록 하는 역할을 합니다.
Continuation
인터페이스의 주요 메서드:
resumeWith(result: Result<T>)
: 중단된 코루틴을 재개합니다.result
는 코루틴이 재개될 때 사용할 결과를 포함합니다. 이는 성공적인 결과일 수도 있고, 예외일 수도 있습니다.interface Continuation<in T> { val context: CoroutineContext fun resumeWith(result: Result<T>) }
resumeWith
resumeWith
메서드는Continuation
인터페이스의 핵심 메서드로, 코루틴이 중단된 지점에서 재개될 때 호출됩니다. 이 메서드는Result
객체를 인수로 받아, 성공적인 결과 또는 예외를 처리합니다.
Result.success(value: T)
: 성공적인 결과를 나타냅니다.Result.failure(exception: Throwable)
: 예외를 나타냅니다.fun <T> Continuation<T>.resumeWith(result: Result<T>) { // 실제 구현은 코틀린 코루틴 라이브러리에 의해 제공됨 }
예제 코드
다음은
Continuation
과resumeWith
를 사용하여 코루틴을 중단하고 재개하는 예제입니다.import kotlin.coroutines.* suspend fun findUser(): String = suspendCoroutine { continuation -> println("Finding user...") // 비동기 작업 후 결과를 반환 continuation.resumeWith(Result.success("User123")) } suspend fun findProfile(user: String): String = suspendCoroutine { continuation -> println("Finding profile for $user...") // 비동기 작업 후 결과를 반환 continuation.resumeWith(Result.success("Profile123")) } suspend fun findImage(profile: String): String = suspendCoroutine { continuation -> println("Finding image for $profile...") // 비동기 작업 후 결과를 반환 continuation.resumeWith(Result.success("Image123")) } fun main() = runBlocking { val user = findUser() val profile = findProfile(user) val image = findImage(profile) println("Found image: $image") }
설명
- suspendCoroutine: 이 함수는 코루틴을 중단하고,
Continuation
객체를 사용하여 나중에 재개할 수 있도록 합니다.- Continuation.resumeWith: 비동기 작업이 완료되면
resumeWith
메서드를 호출하여 코루틴을 재개합니다.Result.success
를 사용하여 성공적인 결과를 전달합니다.이 예제에서는
findUser
,findProfile
,findImage
함수가 각각 비동기 작업을 수행하고, 결과를resumeWith
메서드를 통해 전달하여 코루틴을 재개합니다. 이를 통해 연속적인 비동기 작업을 간단하고 직관적으로 처리할 수 있습니다.
Continuation Passing Style (CPS) ?
Continuation Passing Style (CPS)는 함수 호출의 제어 흐름을 명시적으로 나타내기 위해 사용되는 프로그래밍 스타일입니다. 함수는 자신의 작업을 수행한 후 결과와 함께 다음에 수행할 작업(즉, 연속 작업)을 나타내는 continuation을 인수로 전달받습니다. 이는 비동기 작업과 중단 가능한 작업을 처리하는 데 특히 유용합니다.
특징
- 제어 흐름의 명시적 전달: CPS에서는 함수가 완료된 후 수행할 작업을 continuation으로 명시적으로 전달합니다.
- 비동기 작업 처리: 비동기 작업이 완료된 후 수행할 작업을 continuation으로 표현하여 비동기 프로그래밍을 단순화합니다.
- 재귀적 호출 제거: CPS는 재귀적 함수 호출을 명시적인 제어 흐름으로 바꾸어 꼬리 재귀 최적화와 유사한 효과를 얻을 수 있습니다.
예제 코드
다음은 간단한 CPS 스타일의 코드 예제입니다.
// 일반적인 함수 호출 스타일 fun add(x: Int, y: Int): Int { return x + y } // CPS 스타일의 함수 호출 fun addCPS(x: Int, y: Int, continuation: (Int) -> Unit) { val result = x + y continuation(result) } // 사용 예시 fun main() { addCPS(3, 5) { result -> println("The result is $result") } }
코루틴과 CPS
코틀린의 코루틴은 내부적으로 CPS와 유사한 방식으로 동작합니다. 코루틴이 중단(suspend)될 때 현재 상태를
Continuation
객체에 저장하고, 나중에 재개(resume)될 때 이Continuation
객체를 사용하여 이전 상태에서 계속 실행합니다.