TIL

9강 코루틴과 Continuation

Continuation

class UserService {

  private val userProfileRepository = UserProfileRepository()
  private val userImageRepository = UserImageRepository()
  
  suspend fun findUser(userId: Long): UserDto {
    // 0단계 - 초기 시작
    
    // 1차 중단
    val profile = userProfileRepository.findProfile(userId) 
    
    // 1단계 - 1차 중단 후 재시작
    
    // 2차 중단
    val image = userImageRepository.findImage(profile) 
    
    // 2단계 - 2차 중단 후 재시작
    return UserDto(profile, image)
  } 
}
// 점점 더 많은 기능들이 추가될 것이다.
interface Continuation {
}
suspend fun findUser(userId: Long): UserDto {
  val sm = object : Continuation {
    var label = 0
    
    // label == 2일 때 아래에서 UserDto를 반환하기 위해 필요
    var profile: Profile? = null
    var image: Image? = null
  }
  
  when (sm.label) {
    0 -> { // 0단계 - 초기 시작
      sm.label = 1
      
      sm.profile = userProfileRepository.findProfile(userId)
    }
    1 -> { // 1단계 - 1차 중단 후 재시작
      sm.label = 2
      
      sm.image = userImageRepository.findImage(sm.profile!!)
    }
    2 -> { // 2단계 2차 중단 후 재시작
      return UserDto(sm.profile!!, sm.image!!)
    } 
  }
}
interface Continuation {
  suspend fun resumeWith(data: Any?)
}
suspend fun findUser(userId: Long, continuation: Continuation?): UserDto {
  val sm = continuation ?: object : Continuation { // 무한 루프를 피하기 위해 null인 경우에만 label==0인 sm 생성
    var label = 0
    var profile: Profile? = null
    var image: Image? = null
    
    override suspend fun resumeWith(data: Any?) {
	    when (label) { // Continuation 내부에서 label을 증가
	      0 -> {
  	      profile = data as Profile
  	      label = 1
  	    }
	      1 -> {
	        image = data as Image
	        label = 2
	      }
	    }
      findUser(userId, this) // Continuation 자신을 전달
    }
  }
  
  when (sm.label) {
    0 -> {
      userProfileRepository.findProfile(userId, sm) // sm 전달
    }
    1 -> {
      userImageRepository.findImage(sm.profile!!, sm) // sm 전달
    }
  }
  return UserDto(sm.profile!!, sm.image!!)
}

class UserProfileRepository {
  suspend fun findProfile(userId: Long, continuation: Continuation) { // 리턴값이 필요 없어짐
    delay(100L)
    continuation.resumeWith(Profile())
  } 
}

class UserImageRepository {
  suspend fun findImage(profile: Profile, continuation: Continuation) { // 리턴값이 필요 없어짐
    delay(100L)
    continuation.resumeWith(Image())
  }
}
  1. findUser 최초 호출
    1. label이 0인 sm : Continuation 생성
  2. findProfilesm 전달하며 호출
    1. Profile을 생성 후 sm.resueWith 호출
  3. sm 내부에서 label에 따라 알맞은 결과 데이터 저장
  4. Continuation이 자신을 넘기며 다시 findUser 호출
    1. sm : Continuationnull이 아니므로 생성하지 않고 그대로 사용
    2. label 1인 sm으로 로직 동작
  5. label 2가 되면 결과를 리턴

코루틴의 Continuation

// 실제 Continuation 인터페이스이다.
public interface Continuation<in T> {
  public val context: CoroutineContext
  public fun resumeWith(result: Result<T>)
}