1

Can someone enlighten me about what is going on in this code example:

import kotlinx.coroutines.*
import kotlin.time.*

fun subTask(scope: CoroutineScope): Job {
    val job = scope.launch(Dispatchers.IO){
        println("SubTask started.")
        delay(500L) 
        println("SubTask ending.")
    }
    job.invokeOnCompletion(object: CompletionHandler{
        override fun invoke(cause: Throwable?) {
            println("SubTask completed.")
        }
    })
    return job
}

fun main() {
    val duration = measureTime {
        runBlocking {
            val job = withContext(Dispatchers.IO) {
                subTask(this)
            }
            println("Waiting for SubTask") 
            job.join()
        }
    }
    println("Duration: $duration")
}

(Click here for Kotlin playground)

I was expecting to see

SubTask started.
Waiting for SubTask
SubTask ending.
SubTask completed.
Duration: 600ms

But I am getting

SubTask started.
SubTask ending.
SubTask completed.
Waiting for SubTask
Duration: 600ms

Why isn't the subTask run in parallel with main()?

Robert
  • 6,855
  • 4
  • 35
  • 43

1 Answers1

3

withContext suspends until it is complete. Since you are passing the scope of the withContext to subTask() and subTask() uses it to launch its job, withContext will suspend until this other launched job completes. If subTask() used a different CoroutineScope, or if you had not wrapped your subTask() call in withContext, you would have had your expected behavior.

Tenfour04
  • 83,111
  • 11
  • 94
  • 154
  • Will `withContext` force it to wait till it finishes with this job, meaning that if I start another job somewhere else, it won't be launched till I finish with this one? I want to have some queue of operations to be handled: https://www.reddit.com/r/android_devs/comments/w4fnbj/need_some_tips_about_reducing_anr_by_switching_to/ . I was told that even if I use a single thread for Coroutines, it will try to go to other jobs. – android developer Jul 24 '22 at 06:45
  • Starting a job somewhere else will have no relation to `withContext` in the current coroutine. All `withContext()` does is switch the coroutine context used for the code inside its lambda. Coroutines internal code is synchronous regardless of whether you’re using `withContext`. It will not help you create a queue. To do a queue, I think you would need to launch each coroutine using CoroutineStart.Lazy and pass the returned Job to a Channel. A single coroutine can loop through the channel and `join()` each Job in the queue. – Tenfour04 Jul 24 '22 at 12:37
  • It won't? But someone said it will, as it will suspend and let the thread work on something else. How would `CoroutineStart.Lazy` be used? So many APIs are being talked about when I asked this question... Can you please reach the reddit post and suggest your solution there? – android developer Jul 25 '22 at 06:39
  • Zhuniden (known as EpicPandaForce on this site) was answering the question in your post title (how to prevent ANR) and not your question about creating an orderly job queue, which was very briefly mentioned deep in the text of your post. `withContext` in his example switches off the main thread to free it up while doing background work. But you could have multiple concurrent coroutines doing this, which is not a queue. If you post a question on this site I’ll answer with code that shows how to do a queue. And you might get some other good suggestions. – Tenfour04 Jul 25 '22 at 12:59
  • I wasn't talking about the UI thread. The UI thread can do whatever it wishes. Should always be free as soon as possible. I was talking about the background thread that needs to handle the queue of operations that get accumulated from the UI thread. – android developer Jul 26 '22 at 07:20
  • Here, created a new question: https://stackoverflow.com/questions/73119442/how-to-properly-have-a-queue-of-pending-operations-using-kotlin-coroutines . Please try to make a simple example which works, yet very minimal. – android developer Jul 26 '22 at 07:39