I wonder if the following is a UniFlow bug: <https...
# uniflow
e
I wonder if the following is a UniFlow bug: https://gist.github.com/erikhuizinga/05a2712bdb0f32c45fa192cd137770e0 UniFlow's internals use
CoroutineScope.launch(<http://Dispatchers.IO|Dispatchers.IO>) { action() }
(not literally, but that's what it comes down to). If you create a lot of
action {}
actions quickly, or if your system is slow, then these launched coroutines aren't necessarily run in order. Remember: launching is fire-and-forget. You don't know when the coroutine is actually run! So, is my code just an example of bad practice? Or is my code an example of a UniFlow running actions in an unordered fashion, and is that a bug?
An alternative approach: 1. Start a slow action, e.g. add some delay before setting state 2. Start a fast action immediately after the slow action You will likely see that the effect of the fast action happens before the slow action completes
Scratch that last comment, the
actor
in the uniflow internals should ensure processing of actions in the order they are sent to it, no matter how fast or slow the actions run. However, my original bug still stands: UniFlow uses
launch
to ultimately send actions to the actor, which may cause actions going out of order
And that seems like a 🐛 bug to me in a state machine with action inputs and state/event outputs!
a
Hey @Erik what version do you use?
UniFlow uses 
launch
 to ultimately send actions to the actor, which may cause actions going out of order
Ok, interesting 🤔
e
For the version, see the linked gist: 0.11.6
Coroutines 1.3.5 (but that shouldn't matter)
Kotlin 1.4.31
In other words: is it a bug if UniFlow doesn't preserve action order? Because then I can file a bug report
a
Did you tried woth last 0.13.0 alpha?
I agree it should preserve order as much as possible
apart if you launch new job without joining them from actions
@Erik here below the new code of the reducer:
there is no
launch
but just a
withContext
to execute directly the function
e
a
withContext can only be called from a coroutines
here we are just enqueing action into the Actor
I can write this:
with a
launchOnMain
e
I understand, it's all because you use
actor.send
which is a
suspend fun
So then I would argue that that shouldn't be used, but
offer
should be used instead. The buffer is there for a reason: we should limit concurrency in some way, because it's not free. When the action buffer is full, we can either suspend, ignore the action, or worse: throw.
Suspending requires launching a coroutine. Launching many coroutines doesn't provide order guarantees, unless you can maybe launch them in some scope/context that preserves launch order?
So either do not launch and
offer
, or use a different scope/context.
runBlocking
would work, but that could block the user, so not an option
a
interesting catch 👍
offer
could block then ... but we avoid the coroutines lunch just for offering
e
I would maybe just log an exception when the buffer is full
But not block. It would lose actions though. Not sure what's better. Maybe this behaviour can be configurable? Just like the shared flow on buffer overflow behaviour
a
Or just log an error? 🤔
as if we throw an exception we can’t do anything either
Can go with something simple like that:
e
Sure
So, in conclusion: is this (being) picked up, or do I need to do anything, e.g. create an issue?
a
already on master 💪
I’m polishing some last stuff to make the 1.0.0