Lorenzo Testa
08/05/2019, 10:42 AMClosedSendChannelException: Channel was closed
, can someone explain it to me?
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
@UseExperimental(ExperimentalCoroutinesApi::class)
fun main() = runBlocking {
val scope = CoroutineScope(Job())
val f = channelFlow {
launch(scope.coroutineContext) {
repeat(10) {
delay(200L)
send(it)
}
}
}
f.collect {
println(it)
}
delay(3000L)
}
Marko Mitic
08/05/2019, 10:45 AMlaunch
ruins your day hereLorenzo Testa
08/05/2019, 10:45 AMLorenzo Testa
08/05/2019, 10:48 AMwithContext
only when calling send
to set the scope given by channelFlow
but never emits anythingMarko Mitic
08/05/2019, 10:49 AMMarko Mitic
08/05/2019, 10:51 AMLorenzo Testa
08/05/2019, 10:55 AMgildor
08/05/2019, 11:01 AMgildor
08/05/2019, 11:02 AMMarko Mitic
08/05/2019, 11:03 AMchannelFlow
will wait on the all coroutines launched from it's scope to finish, you just overrode the scope with your coroutineContextAlexjok
08/05/2019, 1:26 PMfun main() = runBlocking {
val f = channelFlow {
launch {
repeat(10) {
delay(200L)
send(it)
}
}
}
f.collect {
println(it)
}
delay(3000L)
}
Lorenzo Testa
08/05/2019, 1:29 PMlaunch(scope.coroutineContext) {
f.collect {
println(it)
}
}
gildor
08/05/2019, 1:31 PMlaunch(scope.coroutineContext)This is wrong for 99% of cases (and I know 0 cases for rest 1%)
gildor
08/05/2019, 1:32 PMfun main() = runBlocking {
val f = channelFlow {
launch {
repeat(10) {
delay(200L)
send(it)
}
}
}
f.collect {
println(it)
}
}
gildor
08/05/2019, 1:33 PMfun main() = runBlocking
with suspend fun main()
, you don’t need any scope for this, you don’t have any background jobLorenzo Testa
08/05/2019, 1:33 PMgildor
08/05/2019, 1:34 PMcollect
, it’s how cancellation propagatedgildor
08/05/2019, 1:34 PMchannelFlow
, you should control cancellaton of collect
insteadLorenzo Testa
08/05/2019, 1:36 PMcollect
instead on put launch inside channelFlow
, maybe it wasn't clear or I'm not understanding what you're explaininggildor
08/05/2019, 1:38 PMlaunch(scope.coroutineContext)
is wronggildor
08/05/2019, 1:38 PMscope.launch { }
is what you needLorenzo Testa
08/05/2019, 1:39 PMMarko Mitic
08/05/2019, 1:39 PMgildor
08/05/2019, 1:40 PMLorenzo Testa
08/05/2019, 1:40 PMgildor
08/05/2019, 1:40 PMgildor
08/05/2019, 1:41 PMgildor
08/05/2019, 1:42 PMgildor
08/05/2019, 1:43 PMgildor
08/05/2019, 1:47 PMbecause you already launch on another scope, and than replace job!
runBlocking
already provides scope and you run launch
there, but than replace it with another Job, so you broke StructuredConcurrency, launch is not a part of parent scope anymore so parent scope may finish before your background coroutine (this is what happening in your first message) and instead will finish when new scope job will be cancelled, it’s not only confusing and error prone, but also I see no reason to do that, you should use parent scope in most of casesLorenzo Testa
08/05/2019, 1:54 PMLorenzo Testa
08/05/2019, 1:54 PMgildor
08/05/2019, 2:02 PMbut I’m not sure that there is no reason to do thatI really don’t think that there is any valid use case for this. All coroutine builders designed to be launch only on particular scope,
launch
is extension function of CoroutineScope, so you cannot call launch
without scope on receiver.
Of course, you can try to inherit scope of one scope and replace some parts of it, but i would be much more explicit in this case and pass particular elements of scope, but I also don’t have any valid use cases for this