https://kotlinlang.org logo
#coroutines
Title
# coroutines
d

dimsuz

06/21/2020, 5:31 PM
This code:
Copy code
runBlocking {
  val state = MutableStateFlow(0)
  launch { state.value = 1 }
  launch { state.value = 2 }
  launch { state.value = 3 }

  state.collect {
    println("received $it")
  }
}
prints
Copy code
received 0
received 3
Is there a way to receive all values and also have the property of
StateFlow
that new collections will receive the last value?
a

Adam Powell

06/21/2020, 5:40 PM
What's your use case? Wanting both of those properties is often a sign that the idea of what is state and what are events in your system is getting mixed up, and it will cause conceptual/architectural problems later.
d

dimsuz

06/21/2020, 5:47 PM
Use case is that I have some reactive domain model:
Copy code
enum State { Working, Done }

class AuthFeature {
  fun login() {
    state.value = Working
    performRequest()
    state.value = Done
  }
  fun loginState(): Flow<State>
}
And my UI observes the
loginState()
if performRequest() is fast, (cached), state is lost. I tried added
launch
or
yield
, it doesn't help.
I have just read about
SharedFlow
which doesn't conflate values and waits for consumers to process them, so I guess I'll have to wait for it.
a

Adam Powell

06/21/2020, 5:51 PM
that sounds like correct behavior. Your UI should not need to see every state; the most recent state should be enough.
d

dimsuz

06/21/2020, 5:53 PM
Well, sometimes it does. For example if I have more states, like Error, I want do be sure that UI shows error. Or if I have analytics or something, I want to log all states. It should be up to consumer what to ignore.
or at least configurable by consumer.
z

Zach Klippenstein (he/him) [MOD]

06/21/2020, 5:59 PM
You
Error
state isn’t really a state then, it’s an event.
☝️ 7
a

Adam Powell

06/21/2020, 6:01 PM
Exactly. It sounds like you have an event stream that somewhere downstream gets aggregated into one or more states for your UI to render, and some of those states have different rules.
d

dimsuz

06/21/2020, 7:48 PM
Why? let's say I have a screen with a text field. Upon subscribing to the flow above I want it to show strings "Working, Error, Done", just as they are emitted, so that user sees every state transition. Code above would show only Done state. Imagine this in React/Compose.
loginState()
is my props, I want to render it.
z

Zach Klippenstein (he/him) [MOD]

06/21/2020, 7:59 PM
A state would be something like "an error is being shown on the screen". So you could, eg, have a DoneSuccessful state and a DoneError state that includes the error message.
2
a

Adam Powell

06/21/2020, 8:01 PM
Right, or you could have state A that represents, "request in flight" or, "request done" and state B that represents, "the current log/queue of errors not yet acknowledged by the user"
The log is state, but it was built by observing each error event as it happened. The log is enough to render a UI without replaying the events that produced it.
☝️ 1
d

dimsuz

06/21/2020, 8:17 PM
Yeah, that's exactly how I do it. "Working" state gets rendered to a progress bar, "Error" state has a message that gets rendered. I was surprised to discover that some of them are not delivered when I switched to coroutines, it didn't happen with Rx. Let me think about why that might be a problem for me, this change in behavior. Perhaps event/state role are complected somewhere, that's valuable advice! You could say this is a reactivity-specific code smell 🙂
By the way, check out this comment by
airbnb/MvRx
, they had to invent their own abstraction to make sure all state emissions are delivered: CoroutineStateStore
a

Adam Powell

06/21/2020, 8:30 PM
Yep, they'll run into problems this creates for all of the same reasons. 🙂 State is declarative. If missing state B from sequence [A, B, C] by jumping straight to C would cause errors in behavior, the design is one of events, not state.
It's a very common hurdle but much like with structured concurrency vs. alternatives, once you classify the shape of the problem it's almost magical how many other hard edge cases melt away or gain much simpler answers
2
d

dimsuz

06/21/2020, 8:52 PM
Yeah, I've just ran over some of our usecases with my colleague and it turned out some of them were solved exactly by lifting some states to be explicit events. Although we didn't classify them as such, this was done based on intuition. Thank you very much for this mental model, will definitely help in the future.
👍 1
4 Views