Hi! I am trying to implement some sort of event st...
# coroutines
a
Hi! I am trying to implement some sort of event store / redux store using corutines in android application Here is the pseudocode:
Copy code
class Store {
  private val channel = Channel<Action>()

  suspend func dispatch(action: Action) {
    channel.send(action)
  }

  init {
    GlobalScope.launch {
      for (action in channel) {
        reduce(action)
        notifyObservers()
      }
    }
  }
}
Sadly, this code doesn't work - actions are not reducer. Small detail: If i will explicitly set Dispatchers.Main in init and in dispatch - it work, but defeats my purpose. My goal is to move action processing of main thread. 
notifyObservers
 will build 
Props
 for each active 
ViewModel
 and publish.
z
actions are not reduced
Can you elaborate? Are they not being sent on the channel? Not being received from the channel? Is
reduce
not working as expected?
Also, don't use
GlobalScope
.
a
dispatch
remains suspended,
reduce
never called as nothing was send to the channel. Why? And what should I use instead? I just want to offload processing from UI thread to not block UI while I calculate next props.
v
I think you need a buffered channel.
or, if you want to process actions one by one so that Main thread waits for the result, just
async(Dispatchers.Default) { reduce(action) }.await()
a
Why? My understanding is that Randevouz channel will keep send suspended until action is not processed, and processor will be suspended while there is no actions in channel.
👍 1
Actions will arrive at any time, I don't want to wait for result of processing. I want to start additional coroutine on background that will process them for me
z
Re GlobalScope, the kdoc on GlobalScope itself discourages its use, and there is a conversation in this channel every couple of days about why. This stackoverflow answer sums it up nicely: https://stackoverflow.com/a/54351785/1502069
You shouldn't need a buffered channel, that code looks like it should process the channel like you expect.
v
I don't see any need for a channel here then.
just execute actions on the Default scheduler, it's simpler.
z
The difference between these two solutions is the channel approach processes actions from multiple senders sequentially, and the async approach processes them concurrently. Is that sequential behavior a requirement?
v
your solution allows executing actions one-by-one, like an actor. The ^^^ allows using CPU count parallel executions.
exactly
or, if actions are IO-bound, you can use Dispatchers.IO to do a lot of them in parallel.
a
Yes, sequential processing is a strong must.
I need "Fan-In" processing, If I understand docs correctly.
v
ok, then show more code (who calls
dispatch
, who calls the Store ctor?)
a
dispatch
is called by UI - whenever something happens - new action is sended to dispatch.
Store
ctor is created on application start - it is single instacnce per application
Also dispatch can be called from some IO operations, with result of the operation.
reduce
change global mutable state, so I need to protect it from parallel mutation - that's why sequential order is a must
z
Does the
channel.send
function get invoked?
a
Copy code
func dispatch(action: Action) {
  log("Sending")
  channel.send(action)
  log("Sended")
}
I see single "Sending" message but not "Sended"
z
Can you post some actual code? You're typing
func
so I know you're not copy/pasting 😜
a
Re GlobalScope> Thanks for links. I am still unsure what I should replace it with. Should I make
suspend init
? Should I use MainScope?
z
I would create a
Copy code
private val scope = CoroutineScope(Dispatchers.Default)
In your class and use that. That makes it explicit, and also means if you need to ever dispose your
Store
class (eg to reset state between tests) you can just cancel that scope.
💯 1
a
Yeah it is waaaay to simplified code. I rather want to check that my knowledge of corutines is correct. I did not "own" this codebase, just helps with architecture. I can try to craft minimal actual example and share to you, if you will have time to read the code. I hoped that I did some rookie mistake that somebody could see on the spot. But looks like I need to go on the hard road 🙂
z
I'm guessing there's some detail in the actual code that is the culprit. The general idea looks fine.
a
That is great news! I will experiment with clean from scratch example.
r
instead of implementing your own, you can also try https://github.com/freeletics/CoRedux
a
Cool! Same Idea! Will look for reference code, thanks.