Unexpected tryEmit behaviour
See original GitHub issuetryEmit
doesn’t attempt to emit a value after first subscriber joined and returns false
.
Setting replay
or extraBufferCapacity
> 0 or replacing tryEmit
by emit
resolves the issue
@Test
fun tryEmitExample() = runBlocking {
val sharedFlow = MutableSharedFlow<Long>()
val asyncReceiver = async(){
delay(300)
sharedFlow.collect{
println("Received on 1 $it")
}
println("Done")
}
repeat(4) {
delay(100)
val res = sharedFlow.tryEmit(System.currentTimeMillis())
println("Emitted ${System.currentTimeMillis()} Subscribers: ${sharedFlow.subscriptionCount.value} try: $res")
}
asyncReceiver.cancel()
}
Emitted 1605303026489 Subscribers: 0 try: true
Emitted 1605303026592 Subscribers: 0 try: true
Emitted 1605303026693 Subscribers: 1 try: false
Emitted 1605303026799 Subscribers: 1 try: false
@Test
fun emitExample() = runBlocking {
val sharedFlow = MutableSharedFlow<Long>()
val asyncReceiver = async(){
delay(300)
sharedFlow.collect{
println("Received on 1 $it")
}
println("Done")
}
repeat(4) {
delay(100)
sharedFlow.emit(System.currentTimeMillis())
println("Emitted ${System.currentTimeMillis()} Subscribers: ${sharedFlow.subscriptionCount.value}")
}
asyncReceiver.cancel()
}
Emitted 1605303080955 Subscribers: 0
Emitted 1605303081061 Subscribers: 0
Received on 1 1605303081162
Emitted 1605303081166 Subscribers: 1
Received on 1 1605303081267
Emitted 1605303081267 Subscribers: 1
Issue Analytics
- State:
- Created 3 years ago
- Reactions:2
- Comments:17 (6 by maintainers)
Top Results From Across the Web
MutableSharedFlow is kind of complicated | by Lukas Vyletel
What tryEmit method does, in order to not block thread until an event is collected, is that it sends a value to MutableSharedFlow...
Read more >Do or do not; there is no tryEmit() - Dan Lew Codes
On top of that, tryEmit() returns false , indicating that it knows it ... replay cache and using it will not result in...
Read more >My MutableStateFlow Didn't Emit! - Handstand Sam
Use immutable data structures (classes with all val properties) with MutableStateFlow to avoid unexpected behavior.
Read more >Rx to Coroutines Concepts, Part 5: Shared Flows
And if you don't have any buffer, tryEmit won't do anything except wake up active collectors who are currently suspended. It will usually...
Read more >Reactor 3 Reference Guide
Each operator adds behavior to a Publisher and wraps the previous step's ... When using the tryEmit* API, parallel calls fail fast.
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
I think people who are new to coroutines and Kotlin Flow in general would be confused about this as well. I myself was surprised when I encountered it first (and I’m not even new to reactive frameworks or coroutines) and so was a colleague of mine. I think this is confusing to anyone with little experience.
You’re describing different situation. I am not suggesting to define non-zero default. I’m suggesting to force developer to select values by themselves, to force them to think about it.
In my projects a sensible default might be
MutableSharedFlow(replay = 0, extraBufferCapacity = 1, bufferOverflow = BufferOverflow.DROP_OLDEST)
This might not serve everyone, so I don’t want this to be the default, but at least to me, I NEVER want
MutableSharedFlow(replay = 0, extraBufferCapacity = 0, bufferOverflow = BufferOverflow.SUSPEND)
. It would rendertryEmit
completely useless, which I don’t want probably 100% of time. Should anybody find it necessary or useful, they will still be able to do so, they’ll just specify the buffer capacity, to ensure they understand implications of their decision.@elizarov I would like to renew discussion about this one.
First of all, the documentation about default implementation of
MutableSharedFlow()
is positioned aboveSharedFlow
. To me, this is completely unexpected. The way I usually look at the documentation is, that I cmd click on the type, that I am working with and look through documentation there. So I’d click on the invocation ofMutableSharedFlow
and read what it has to offer. That explanation is not present there at all, so I have completely overlooked it until I found this issue (which I had been specifically searching for).I’d at least suggest to either move that piece of documentation above the function
MutableSharedFlow
, or if nothing else, add a link from documentation ofMutableSharedFlow
to documentation ofSharedFlow
with the explanation, that some concepts regarding theextraBufferCapacity
and default values are explained inSharedFlow
’s documentation. (I’d probably vote for the former solution)Second, the default implementation of
MutableSharedFlow
is unexpected to me. Probably nobody wants that implementation. I can’t imagine there are many developers, who would expect, that if they create their flow like this:val testFlow = MutableSharedFlow<String>()
, it means that all attempts totryEmit()
will immediately drop the event. Even if developers would expect that, it’s rarely the case that they would want that default behavior.So I’d suggest to remove the default parameter values at least for
replay
andextraBufferCapacity
parameters and require developers to provide some values of their own, to match their needs. Default values make sense if a sensible default is chosen, but personally I don’t believereplay=0, extraBufferCapacity=0
is a sensible default. With my suggestions, everybody will have to think about what values to choose and that way they’ll avoid bugs in advance. (Somebody just starting to learn about coroutines API is guaranteed to use default implementation of MutableSharedFlow and expecttryEmit
to work, they won’t think twice about it)What do you think about these suggestions?
Also, what do other devs think?