Hey all ! A question / discussion :slightly_smilin...
# coroutines
m
Hey all ! A question / discussion šŸ™‚
Copy code
private var job: Job? = null
        set(value) {
            println("Setting value")
            field = value
        }

        job = lifecycleScope.launch() {
            println("Started")

            try {
                println("Try")
            } catch (e: CancellationException) {
                println("Catch")
            } finally {
                println("Finally")
            }
        }
prints
Copy code
Started
Try
Catch
Finally
Setting value
in order. Which means when using default coroutine start,
launch
returns
job
reference after contents are executed ( if execution is fast enough ). So thereā€™s no guarantee when youā€™ll be handled
job
reference when you do the assignment. I didnā€™t find any mention of this in any docs, but I was expecting it to at least wait for reference to be handled before starting execution šŸ¤” This is probably troublesome in scenarios if you do an
if ( job != null ) return
check or similar ( clear in
finally
(
finally { job = null }
). You may end up
job
being assigned and not getting cleared. P.S. Using
CorotuineStart.LAZY
followed by
job?.start()
resolves this issue, Iā€™m just asking if is this expected from default implementation šŸ¤” ?
w
This is Android, right? It might be because the default UI dispatcher uses an
immediate
scheduler, which executes the contents in-place if the coroutine is already started on the UI thread (unless a suspension point is hit)
šŸ‘ 1
e
IIRC lifecycleScope uses Dispatchers.Main.immediate, so if you're already on the main thread it'll run immediately
*what Łukasz said
m
yeah, spot on itā€™s android šŸ˜„
e
also it seems bad to rely on that ordering anyway, if it's a multi-threaded dispatcher then there's still a chance the job could complete before you get done assigning the field
btw, why do you want to clear the job anyway?
m
yeah, I came up with snippet just for demonstration, to be honest I was mentally convinced that assignment should be done before execution šŸ˜„
to prevent multiple invokes of a function, eg
Copy code
fun something() {
   if ( job != null ) return

   job = launch { ... }
}
or cancelling previous job and reassigning ( for cases like refresh spam etc. )
it was working just fine until I hit the point where I return from the launch early by adding some checks, it turned into a race condition
e
var
-ectomy?
Copy code
val parentJob = Job(lifecycleScope.coroutineContext.job)

parentJob.cancelChildren() // cancel previous jobs without races
lifecycleScope.launch(parentJob) {
    // do work
}