Suspending until StateFlow reaches one of the desired states and returning the result

Viewed 239

Consider a sealed class State.

sealed class State {
    object Unknown : State()
    object Loading : State()
    object Success : State()
    data class Failure(val exception: Exception)
}

I have a stateflow where consumers can actively listen to the state updates.

val state:State = MutableStateFlow(State.Unknown)

Now, I also want to have a simple suspend method which waits till the state reaches either Success or Failure, so consumers who just need the result once need not be aware of the stateflow.

How to achieve this?

2 Answers

Although you already came up with a working solution, you might want to make use of the built-in Flow.first { ... } operator for simplicity.

suspend fun waitForResult(): State  {
    val resultStates = setOf(State.Success::class, State.Failure::class)
    return state.first { it::class in resultStates }
}

I was able to come up with the following extension function which looks to be working fine.

suspend fun waitForResult(): State  {
    val resultStates = setOf(State.Success::class, State.Failure::class)
    return state.waitForStates(resultStates)
}

suspend fun <T : Any> StateFlow<T>.waitForStates(states: Set<KClass<out T>>): T = coroutineScope {
    var currentValue = value

    // not needed for correctness, just an optimisation
    if (currentValue::class in states) {
        return currentValue
    }

    coroutineScope {
        collect {
            if (it::class in states) {
                currentValue = it
                cancel()
            }
        }
    }

    return currentValue
}
Related