:wave::skin-tone-2: Hey everyone, I'm looking to c...
# coroutines
c
👋🏻 Hey everyone, I'm looking to create a
CoroutineScope
which I want to run in the background while it's waiting for updates to be sent via a Channel, but once the update is sent, I want the result to be executed on the main thread. In that case, would I want to use
Dispatchers.Main.immediate
, something like this
CoroutineScope(Dispatchers.Main.immediate + Job())
? I think I'm getting a little hung up as I'm not sure when I would have a Scope that doesn't use that dispatcher (since I can launch a suspending function with the scope, but telling it to switch to a different dispatcher, say
IO
).
z
If you’re just calling
receive()
on a channel and waiting for a value to come in, you don’t need to use a special dispatcher for that. You can just receive from the channel on the main dispatcher.
o
if you use
immediate
dispatcher it might dispatch immediately if the message is also posted from the main thread, but that can be dangerous. if the messages are only sent from non-main threads, then there's no reason to use
immediate
additionally, every scope implicitly has a
Job()
, you don't need to add one explicitly unless you prefer it that way 🙂
🙇🏻‍♂️ 1
c
@Zach Klippenstein (he/him) [MOD] I think I may see what you're talking about, instead of having a custom scope it'd just be using
MainScope()
?
z
Sure, that's one way to do it. The IO dispatcher is for blocking calls that block the current thread by not doing any work, but just sitting there waiting for the OS to return a value.
Channel.receive()
is a suspending call, which means it doesn't block the thread, and can thus be invoked from the main thread.
c
Yeah but
receive
can only be invoked from another suspending function so I need to use
CoroutineScope.launch
somewhere. So from the consumer side it's essentially this:
Copy code
abstract class FooStatusConsumer(
    coroutineScope: CoroutineScope,
    channel: Channel<FooStatus> = Channel()
) {
    init {
        coroutineScope.launch(<http://Dispatchers.IO|Dispatchers.IO>) {
            for (fooStatus in channel) {
                withContext(Dispatchers.Main) {
                    consume(fooStatus)
                }
            }
        }
    }

    abstract fun consume(fooStatus: FooStatus)
}
o
why not
Dispatchers.Main
at the top, drop
withContext
?
c
That's what I'm trying to figure out. My understanding is I'd want to switch to the IO dispatcher before I start IO work which I would think waiting for channel updates would warrant that. Before sending updates though I would want to ensure I'm doing so from Main which is why I have a second context
It appears to work without switching dispatchers though, so I'm guessing waiting for responses from a channel doesn't require switching to the IO dispatcher 🤷🏻‍♂️
o
that's what zachklipp was getting at though -- suspend functions don't block threads
that's why it's an extremely useful concurrency model, because you don't have to switch your context whenever you do something that might be blocking -- if instead you know it will be suspending, you can stay in the same context but free up the thread while it waits
c
I getcha, for whatever reason your message made a bit more sense. So if it's just calling a suspend function, as long as you
launch
from a scope then you are good to go. I'm guessing if I wanted to kick off the suspend function from the IO or Default dispatcher that's when I'd use a scope which started with that... But in the majority of practical situations
MainScope()
is probably a decent place to start
Thanks Octavia and Zach 🙇🏻
o
yes, usually I remain in one dispatcher for the most part, and only if I need to do something blocking do I
withContext(<http://Dispatchers.IO|Dispatchers.IO>)
-- whether that be the Main or Default dispatcher depends on which part of the code I'm in
z
In fact, using the IO dispatcher just to receive from a channel could actually be slower, because if something is already in the channel then it has to jump to the IO pool’s queue, and then back into the main queue, instead of just pulling the next available item out immediately and starting to process it.
c
Awesome, thanks guys this makes so much more sense now 🙇🏻