Can someone explain to me why this code does not w...
# coroutines
s
Can someone explain to me why this code does not work with a
delay(500L)
but will intermittently work with
delay(499L)
or shorter durations? https://play.kotlinlang.org/#eyJ2ZXJzaW9uIjoiMS44LjAiLCJwbGF0Zm9ybSI6ImphdmEiLCJhcmdzIj[…]AgICAgICAgIHByaW50bG4oZSlcbiAgICAgIH1cbiAgfSAgIFxufVxuIn0= code in thread as well
🧵 1
Copy code
/**
 * You can edit, run, and share this code.
 * <http://play.kotlinlang.org|play.kotlinlang.org>
 */

import kotlinx.coroutines.*

private var job: Job? = null
private var scope = CoroutineScope(<http://Dispatchers.IO|Dispatchers.IO>)

val handler = CoroutineExceptionHandler { _, exception -> 
    println("CoroutineExceptionHandler got $exception") 
}

fun main() {
       
    test("1")
    test("2")
    test("3")

}

fun test(label: String) {
  job?.cancel()
  job = scope.launch(handler) { 
  try {
    println("starting")
    delay(500L)
    println(label)
  } catch (e: Exception) {
          println(e)
      }
  }   
}
l
The first two jobs obviously get cancelled immediately. As soon as the third is launched, main exits, which seems to cancel the third job. Try adding
Copy code
runBlocking {
        delay(1000)
    }
to the end, and it works every time.
e
or better yet, use
coroutineScope
or similar so that you can properly await all active jobs
e.g. this won't end until the non-cancelled jobs are complete
Copy code
suspend fun main(): Unit = coroutineScope {
    test("1")
    test("2")
    test("3")
}

fun CoroutineScope.test(label: String) {
    job?.cancel()
    job = launch(handler) { 
        try {
            println("starting")
            delay(500L)
            println(label)
        } catch (e: Exception) {
            println(e)
        }
    }  
}
s
Thanks, this makes sense. Idk why i thought delay was blocking
l
delay is blocking, but it blocks the current coroutine. When you launch, it creates a new coroutine.
Think of it like launching a new thread.
e
<http://Dispatchers.IO|Dispatchers.IO>
and the other built-in dispatchers are run on a pool of daemon threads, so they do not prevent the JVM from shutting down when
main
returns
c
Btw: don't catch Exception with coroutines
j
delay is blocking, but it blocks the current coroutine
This is not the vocabulary used in this case. Blocking specifically means blocking a thread, Suspending means the current coroutine stops its execution, but the thread that was executing it is now free to do something else.
l
I agree the wording is not quite correct when combining with my 'coroutine = thread' comment. I'm not sure what the correct term is in that case. Would it be better to say it blocks the task? Suspending does indicate that other tasks can run, but my point was that if you think procedurally, as beginners to coroutines often do, it's easier to think of something being blocked instead of suspending execution so something else can run.
l
You can say it's like it's blocking, it only blocks the coroutine, without blocking the actual thread, and that we call this suspending, not blocking.
I know it's less concise… That's quite expected when you introduce a new concept or need to define a word.
l
I remember when I switched some of our old threaded code to coroutines, some people on the team were caught up on the term ‘suspending’, since it’s more or less coroutine specific.
c
At first it seems like these are two words for the sale thing, but in practice it's very different. For example, you can't block the main thread on Android, but it's completely fine to suspend a coroutine being executed on it. It's important developers know which one any method does
I had a hard time with suspension at first because I did not understand how it was physically possible to do something other than block, but that leads to confused code