예외 처리

onErrorReturn / onErrorReturnItem / onErrorResumeNext / retry / retryUntil

예외 처리

onErrorReturn()


에러도 하나의 데이터이기 때문에 예외 발생 시 에러를 의미하는 다른 데이터로 변환 후에 데이터(에러)를 발행해야 한다.

fun main() = runBlocking<Unit> {
    val numbers = listOf("1", "2", "$3", "4", "5")
    
    Observable.fromIterable(numbers)
        .map { Integer.parseInt(it) }
        .onErrorReturn { error ->
            if (error is NumberFormatException) {
                error.printStackTrace()
            }
            return@onErrorReturn -1 // 에러를 의미하는 -1 전달
        }
        .subscribe { item ->
            if (item < 0) {
                println("Error") 
            } else {
                println("item is $item")
            }
        }

    delay(1000)
}


// item is 1
// item is 2
// Error
// java.lang.NumberFormatException: For input string: "$3"



onErrorReturnItem()


onErrorItem() 과 동일하지만 중간에 에러를 넘기는 부분에 대한 구현을 생략할 수 있는 함수이다.

fun main() = runBlocking<Unit> {
    val numbers = listOf("1", "2", "$3", "4", "5")
    
    Observable.fromIterable(numbers)
        .map { Integer.parseInt(it) }
        .onErrorReturnItem(-1)
        .subscribe { item ->
            if (item < 0) {
                println("Error") 
            } else {
                println("item is $item")
            }
        }

    delay(1000)
}


// item is 1
// item is 2
// Error



onErrorResumeNext()


에러 발생 시 다른 Observable 로 대체하여 전달하는 함수이다.

fun main() = runBlocking<Unit> {
    val numbers = listOf("1", "2", "3", "4", "5")

    val onParseError = Observable.defer {
        println("Change Observable")
        Observable.just(-1)
    }.subscribeOn(Schedulers.io())
    
    Observable.fromIterable(numbers)
        .map { Integer.parseInt(it) }
        .onErrorResumeNext(onParseError)
        .subscribe { item ->
            if (item < 0) {
                println("Error") 
            } else {
                println("item is $item")
            }
        }

    delay(1000)
}


// item is 1
// item is 2
// Change Observable
// Error



retry()


에러 발생 시 재시도하는 함수로, 반복할 횟수를 지정할 수 있으며 true 를 반환하면 재시도, false 를 반환하면 중단한다.

fun main() = runBlocking {
    val stream = Observable.range(1, 10)
        .map {
            if (it == 3) {
                throw Exception("Error") 
            } else { 
                it
            }
        }

    println("retry() start")
    
    stream.retry(2)
        .subscribeBy(
            onNext = { println("onNext() - $it") },
            onError = { println("onError() - ${it.message}") }
        )

    println("\nretry lambda start")
    
    stream.retry { retryCount, e ->
        println("retry count : $retryCount")
        if (retryCount > 2) {
            false 
        } else {
            true
        }
    }.subscribeBy(
        onNext = { println("onNext() - $it") },
        onError = { println("onError() - ${it.message}") }
    )
}


// retry(2) start
// onNext() - 1
// onNext() - 2
// onNext() - 1
// onNext() - 2
// onNext() - 1
// onNext() - 2
// onError() - Error

// retry lambda start
// onNext() - 1
// onNext() - 2
// retry count: 1
// onNext() - 1
// onNext() - 2
// retry count: 2
// onNext() - 1
// onNext() - 2
// retry count: 3
// onError() - Error



retryUntil()


에러 발생 시 설정한 조건이 만족할 때까지 재시도하는 함수이다.

fun main() = runBlocking<Unit> {
    val stream = Observable.range(1, 10)
        .map {
            if (it == 3) {
                throw Exception("Error") 
            } else { 
                it
            }
        }
    
    var retryCount = 0
    stream.retryUntil {
        if (retryCount == 3) {
            true
        } else {
            retryCount++
            println("Retry - ${retryCount}\n")
            false
        }
    }.subscribeBy(
        onNext = { println("onNext() - $it") },
        onError = { println("onError() - ${it.message}") }
    )
}


// onNext() - 1
// onNext() - 2
// Retry - 1

// onNext() - 1
// onNext() - 2
// Retry - 2

// onNext() - 1
// onNext() - 2
// Retry - 3

// onNext() - 1
// onNext() - 2
// onError() - Error
essential