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
}