Home > Software engineering >  Kotlin Coroutines Flow catch mechanism
Kotlin Coroutines Flow catch mechanism

Time:03-27

In my sample I'm calling network operation and emitting success case but on error e.g 404 app crashes wihout emitting exception. Surrendering with try catch prevent crashes but I want to pass error till the ui layer like success case.

  suspend fun execute(
    params: Params,
):
        Flow<Result<Type>> = withContext(Dispatchers.IO) {
    flow {
        emit(Result.success(run(params)))
    }.catch {
        emit(Result.failure(it))
    }

}

CodePudding user response:

As you said you can use try-catch but it would break the structured concurrency since it would catch the cancellation exception as well or it would avoid the cancellation exception to be thrown. One thing that you can do is to use an Exception handler at the point where you launch the root coroutine that calls the suspend function.

val coroutineExceptionHandler = CoroutineExceptionHandler { _, exception -> 
    // handle it
}

scope.launch(handler) { // root coroutine
    execute(params)
    somethingThatShouldBeExecutedOnlyIfPreviousCallDoesNotThrow()
}

CodePudding user response:

There is a helpful function runCatching for creating a Result easily, but the problem in coroutines is that you don't want to be swallowing CancellationExceptions. So below, I'm using runCatchingCancellable from my answer here.

This shouldn't be a Flow since it returns a single item.

If run is a not a blocking function (it shouldn't be if you are using Retrofit with suspend functions), your code can simply be:

suspend fun execute(params: Params): Result<Type> = runCatchingCancellable {
    run(params)
}

If it is a blocking function you can use:

suspend fun execute(params: Params): Result<Type> = runCatchingCancellable {
    withContext(Dispatchers.IO) {
        run(params)
    }
}

If you were going to return a Flow (which you shouldn't for a returning a single item!!), then you shouldn't make this a suspend function, and you should catch the error inside the flow builder lambda:

fun execute(params: Params): Flow<Result<Type>> = flow {
    emit(runCatchingCancellable {
        run(params)
    })
}

// or if run is blocking (it shouldn't be):

fun execute(params: Params): Flow<Result<Type>> = flow {
    emit(runCatchingCancellable {
        withContext(Dispatchers.IO) { run(params) }
    })
}
  • Related