I can't figure out why in this code the `createCha...
# coroutines
s
I can't figure out why in this code the
createChannel2
is working but
createChannel1
is not.
Copy code
private suspend fun createChannel1(): ReceiveChannel<Int> = coroutineScope {
    produce {
        (0..20).forEach { send(it.also { println("Emitting $it in channel...") }) }
    }
}

private  fun CoroutineScope.createChannel2(): ReceiveChannel<Int> = produce {
    (0..20).forEach { send(it.also { println("Emitting $it in channel...") }) }
}

suspend fun main(args: Array<String>) = coroutineScope {
    val channel = createChannel2()
    println("Got channel!")
    for (i in channel) {
        println(i)
    }
}
When using the first function, the code outputs
Emitting 0 in channel...
and then freezes. Could somebody explain to me why doesn't it work like that? Why do you need to make it an extension function?
d
Could somebody explain to me why doesn't it work like that?
coroutineScope { ... }
only finishes when all the child coroutines spawned in
...
also finish. By default,
produce
creates a channel without a buffer, so after sending the first element, it needs to wait for the element to be consumed before sending another one. Yet because the
createChannel1
function hasn't finished yet, no one can consume that element. We have a deadlock: the producer coroutine can't finish because its elements are only consumed after
createChannel
, and
createChannel1
can't finish because the producer coroutine is still running. The second function, on the other hand, finishes immediately after creating a producer coroutine. It uses the outer
coroutineScope
as the scope in which that coroutine runs.
s
Hmm, got it, thanks! So, I need to pass the outer scope into the function (either via extension or manually)? I can't just create a new scope like this, because the new scope would lock up?
d
Typically, yes. You can create a new scope using the
CoroutineScope()
function, but that's only useful in advanced scenarios where you have a thing with some specific lifetime and want to make sure all coroutines launched in that scope finish their work inside that lifetime.
coroutineScope {}
is the simple default scenario where the lifetime of the coroutines is the duration of the
coroutineScope
function call.
s
Got it, thank you very much for explanation! So basically, use
coroutineScope
by default and scope-passing if we need to launch some long-running operation? Is this rule of thumb correct?
d
Yes, you pass a scope to the function when the coroutines created there outlive the function.
s
Great, thank you ^_^