Study/RxKotlin

[RxKotlin] Coroutine과 모나드

hegunhee 2021. 10. 11. 15:17

위 글은 코틀린 리액티브 프로그래밍책을 보며 내용을 정리한 내용입니다.
책 : 코틀린 리액티브 프로그래밍
리부 차크라보티 지음
조승진 옮김

 

코루틴


코틀린의 가장 흥미롭고 혁신적인 기능은 코루틴(Coroutine)이다. 

스레드와같이 비동기식, 논블로킹 코드를 작성하는 새로운 방법이지만 더 간단하고 효율적이며 경랑의 솔루션이다.
스케줄러는 RxKotlin을 통해서만 사용할 수 있는 반면 코루틴은 어디에서나 사용할 수 있다.
쉽게 사용할수있고 쉽게 컨텍스트를 변화할수있어서 필자는 코루틴을 더 선호한다. 그리고 가볍기까지도 하다.

 

코루틴은 대단히 혁신적인 기능이며 스레드에 대한 훌륭한 추상화를 제공하고, 컨텍스트를 변경하며
동시성을 쉽게 만들어준다.
RxKotlin은 아직 코루틴을 사용하지않는다.
코루틴은 동시성을 구현하고 싶은 개발자에게 최고의 선택이다.
그리고 이책을 구매한 이유도 RxKotlin도 있지만 Coroutine의 내용도 담고있어서이다.

 

코루틴 시작하기


suspend fun longRunningTsk() :Long { // 1
    val time = measureTimeMillis { // 2
        println("please wait")
        delay(2000) // 3
        println("Delay Over")
    }
    return time
}
fun main(args: Array<String>){
    runBlocking {
        val exeTime = longRunningTsk()
        println("Execution Time is $exeTime")
    }
}

이 코드를 보자. 주석1의 함수는 함수선언에 함수를 일시 중지로 표시하는 suspend 키워드를 사용하였다.
즉 함수를 실행하는 동안은 프로그램은 결과를 기다려야한다.

하지만 메인 스레드를 일시중단하는 것은 허용되지않는다. Android에서 메인 스레드(UI 스레드)가 일시중단된다는것은 곧 앱이 죽는다는 의미다.
measureTimeMills는 안의 블럭을 진행하고 걸린 시간을 계산하여 반환한다. 함수를보면 2초를 지연시킨다.
호출된 함수가 완료될때까지 프로그램을 대기 상태로 만든다. 
그러나 예제에서는 메인 스레드가 대기하게 된다. 메인스레드가 대기하기를 원치않고
비동기 작업을 수행하고 싶을 수 있다. 비동기 작업을위해 다음과 같이 해보자.

 

fun main(args: Array<String>){
    val time = async(CommonPool) { longRunningTsk()} //1
    println("Print after async")
    runBlocking { println("printing time ${time.await()}") } //2
}

 

그리고 주석 (2) time변수가 사용 가능할 때까지 main 함수를 기다리도록 하는 블로킹 코드를 실행한다. 
await 함수의 도움을 받아 이 작업을 수행하는데, runBlocking 코드 블록에게 async 코드 블록의 실행이 완료되
time 변수가 사용 가능할 때까지 기다리게한다.

 

함수형 프로그래밍 : 모나드


모나드의 개념은 아주 추상적인데, 그 정의에 따르면
모나드는 값을 캡슐화하고 추가 기능을 더해 새로운 타입을 생성하는 구조체라고 설명한다.
간단하게 컬렉션 기능중에 map이 하는 기능과 비슷하다고 보면된다.

 

 

fun main(args : Array<String>){
    val maybeValue : Maybe<Int> = Maybe.just(14)// 1
    maybeValue.subscribeBy(
        onComplete = {println("Completed Empty")},
        onError = {println("Error $it")},
        onSuccess = {println("Completed with value $it")}
    )
    val maybeEmpty : Maybe<Int> = Maybe.empty()
    maybeEmpty.subscribeBy(
        onComplete = {println("Completed Empty")},
        onError = {println("Error $it")},
        onSuccess = {println("Completed with value $it")}
    )
}

여기서 maybe는 모나드로서 Int 값을 캡슐화하고 추가 기능을 제공한다.

모나드인 Maybe는 값을 포함할 수도 있고 포함하지 않을 수도 있으며, 값 또는 오류 여부에 관계없이 완료된다.

그래서 오류가 발생했을때는 onError가 호출된다.
오류가 발생되지않고 값이 존재하면 onSuccess가 값과 함께 호출된다.

값이 없고 오류도 없는 경우는 onComplete가 호출된다.
RxKotlin과는 다르게 순서대로 호출되는것이 아닌 종류에따라 호출되는게 다르다
RxKotlin : onNext -> onComplete (에러 발생시 onError) 
maybe : onSuccess or onError or onComplete