Colton Idle
08/14/2023, 11:27 PMTo make everybody’s life simpler we use the following _convention_: suspending functions do not block the caller thread.
The means to implement this convention are provided by theOne thing that still hasn't clicked with me yet is when to use withContext vs launch vs async. I feel like I've basically always just used launch, but have been using withContext as of late, but dont really know why. Any quick tips/rules you use when you're writing kotlin code on when to do what? Specifically what sparked this question was how to make certain functions I'm writing more testable.function.withContext
kevin.cianfarini
08/14/2023, 11:40 PMwithContext
will suspend the caller until the work inside the context switch is done. launch
and async
are explicitly created to run things concurrently. They are different use cases.
The question really comes down to whether you want to have multiple things run concurrently (launch/async) or whether you want to suspend the caller while you switch the context of execution for some block of code (withContext).xoangon
08/15/2023, 12:27 AMlaunch
and async
are 2 of the 3 coroutine builders (together with runBlocking
). Neither of them are suspend functions and they're the starting point to get you into the coroutines world.
withContext
is a suspend fun
that can only be called once on the coroutines world. It will suspend the coroutine from which it was called and inmediately start a new coroutine with the provided CoroutineContext
. It overrides the Job
of the parent coroutine by default. This means that withContext(<http://Dispatchers.IO|Dispatchers.IO>)
is doing something similar to withContext(<http://Dispatchers.IO|Dispatchers.IO> + Job())
yschimke
08/15/2023, 8:07 AMyschimke
08/15/2023, 8:07 AMxoangon
08/15/2023, 8:17 AMIsn't withContext basically a noop if you are on the right threads?
Not really, even if you do
withContext(currentCoroutineContext())
, you are still suspending one coroutine and launching another in the same context.
Whenever a coroutine is suspended, it may restart in another thread of the same pool of theads (unless the case of Dispatchers.Main
, as for that case there's only one thread and the coroutine will resume its execution on it)Sam
08/15/2023, 8:21 AMwithContext
and coroutineScope
are foreground coroutine builders. They start a new job as a child of the current job, and then wait for it and all its children to complete before returning.
• launch
and async
are background coroutine builders. They start a new child job and then return immediately, without waiting for the new job to complete.Sam
08/15/2023, 8:28 AMwithContext(Job())
. A coroutine builder always creates a new job, as a child of the job in its context. By default, a coroutine builder inherits the context from its containing scope. Passing any context elements to the builder replaces the ones inherited from the scope. But the builder will still create a new job, as a child of whatever job was passed to it. The result is that passing a job to withContext()
(or any coroutine builder) basically detaches it from its containing scope, breaking the structured concurrency job hierarchy. Passing a job to a a coroutine builder is generally a bad idea and should be avoided.xoangon
08/15/2023, 8:32 AMColton Idle
08/15/2023, 12:36 PMwithContext
as I've been using coroutines for a while but only now have I started using it to wrap around my database operations.
I wonder if it's just because most coroutine-y work that I've done depended on other libraries that were already main safe (i.e. retrofit)Colton Idle
08/15/2023, 12:41 PMwithContext
and then in my calling code (app code) that's where I would make the decision to wrap it in launch or async if needed.kevin.cianfarini
08/15/2023, 12:54 PMColton Idle
08/16/2023, 8:28 PM