Co routines invocations will wait for the background thread to finish even if result is not used right away.

To me co-routines resembled async/await by their description. Hence I expected the thread that is calling the co-routine to be able to keep going if the result of the thread is not used yet. However, Thus far it when I used just plain co-routine function from main, main would hand off work to the background thread and then wait for the background thread to finish before proceeding to the next step.

Code Example using plain co-routine
import kotlinx.coroutines.*


suspend fun performLongRequest(msg:String): String {
    return withContext(Dispatchers.IO) {
        ThreadUtils.printWithThreadInfo("Within withContext{} (before sleep) input: " + msg)
        ThreadUtils.sleep(500)

        String.format("MessageResultAfterBlockingOperation for [%s]", msg)
    }
}

fun main() {

    ThreadUtils.printWithThreadInfo("WarmUpStatement: Example showing main thread waiting for co-routine to finish before moving on to the next statement.")

    val mainMillisStamp=System.currentTimeMillis();

    ThreadUtils.printWithThreadInfo("within main() before runBlocking {}")

    runBlocking {
        ThreadUtils.printWithThreadInfo("1st print within runBlocking{}")
        ThreadUtils.printWithThreadInfo("2nd print within runBlocking{}")
        val result1 = performLongRequest("1st-request")
        ThreadUtils.printWithThreadInfo("print within main thread right after 1st-network request.")
        val result2 = performLongRequest("2nd-request")
        ThreadUtils.printWithThreadInfo(result1)
        ThreadUtils.printWithThreadInfo(result2)
        ThreadUtils.printWithThreadInfo(
            "Total time taken: " +
                    (System.currentTimeMillis() - mainMillisStamp) + "ms")
    }
}

Command to reproduce:

gt.sandbox.checkout.commit df593bf \
&& cd "${GT_SANDBOX_REPO}/gt-kotlin-sandbox" \
&& cmd.run.announce "gradle run"

Recorded output of command:

> Task :app:compileKotlin UP-TO-DATE
> Task :app:compileJava NO-SOURCE
> Task :app:processResources NO-SOURCE
> Task :app:classes UP-TO-DATE

> Task :app:run
[2023-03-27 07:14:35.791][main-1][36ms] WarmUpStatement: Example showing main thread waiting for co-routine to finish before moving on to the next statement.
[2023-03-27 07:14:35.791][main-1][0ms] within main() before runBlocking {}
[2023-03-27 07:14:35.849][main-1][58ms] 1st print within runBlocking{}
[2023-03-27 07:14:35.849][main-1][0ms] 2nd print within runBlocking{}
[2023-03-27 07:14:35.863][DefaultDispatcher-worker-1-20][14ms] Within withContext{} (before sleep) input: 1st-request
[2023-03-27 07:14:36.381][main-1][518ms] print within main thread right after 1st-network request.
[2023-03-27 07:14:36.381][DefaultDispatcher-worker-1-20][0ms] Within withContext{} (before sleep) input: 2nd-request
[2023-03-27 07:14:36.886][main-1][505ms] MessageResultAfterBlockingOperation for [1st-request]
[2023-03-27 07:14:36.886][main-1][1ms] MessageResultAfterBlockingOperation for [2nd-request]
[2023-03-27 07:14:36.887][main-1][0ms] Total time taken: 1096ms

BUILD SUCCESSFUL in 1s
2 actionable tasks: 1 executed, 1 up-to-date

Adding async/await allows us to execute the requests without blocking.

Code Example as above but with async/await.

import kotlinx.coroutines.*


suspend fun performLongRequest(msg:String): String {
    return withContext(Dispatchers.IO) {
        ThreadUtils.printWithThreadInfo("Within withContext{} (before sleep) input: " + msg)
        ThreadUtils.sleep(500)

        String.format("MessageResultAfterBlockingOperation for [%s]", msg)
    }
}

fun main() {

    ThreadUtils.printWithThreadInfo("WarmUpStatement: Example showing main thread waiting for co-routine to finish before moving on to the next statement.")

    val mainMillisStamp=System.currentTimeMillis();

    ThreadUtils.printWithThreadInfo("within main() before runBlocking {}")

    runBlocking {
        ThreadUtils.printWithThreadInfo("1st print within runBlocking{}")
        ThreadUtils.printWithThreadInfo("2nd print within runBlocking{}")

        val promise1 = async { performLongRequest("1st-request")}
        ThreadUtils.printWithThreadInfo("print within main thread right after 1st-network request.")
        val promise2 = async {performLongRequest("2nd-request")}

        ThreadUtils.printWithThreadInfo(promise1.await())
        ThreadUtils.printWithThreadInfo(promise2.await())
        ThreadUtils.printWithThreadInfo(
            "Total time taken: " +
                    (System.currentTimeMillis() - mainMillisStamp) + "ms")
    }
}

Command to reproduce:

gt.sandbox.checkout.commit 480ae13 \
&& cd "${GT_SANDBOX_REPO}/gt-kotlin-sandbox" \
&& cmd.run.announce "gradle run"

Recorded output of command:

> Task :app:compileKotlin UP-TO-DATE
> Task :app:compileJava NO-SOURCE
> Task :app:processResources NO-SOURCE
> Task :app:classes UP-TO-DATE

> Task :app:run
[2023-03-27 07:17:54.235][main-1][35ms] WarmUpStatement: Example showing main thread waiting for co-routine to finish before moving on to the next statement.
[2023-03-27 07:17:54.236][main-1][1ms] within main() before runBlocking {}
[2023-03-27 07:17:54.301][main-1][65ms] 1st print within runBlocking{}
[2023-03-27 07:17:54.301][main-1][0ms] 2nd print within runBlocking{}
[2023-03-27 07:17:54.303][main-1][2ms] print within main thread right after 1st-network request.
[2023-03-27 07:17:54.322][DefaultDispatcher-worker-1-20][19ms] Within withContext{} (before sleep) input: 1st-request
[2023-03-27 07:17:54.322][DefaultDispatcher-worker-2-21][0ms] Within withContext{} (before sleep) input: 2nd-request
[2023-03-27 07:17:54.841][main-1][519ms] MessageResultAfterBlockingOperation for [1st-request]
[2023-03-27 07:17:54.841][main-1][0ms] MessageResultAfterBlockingOperation for [2nd-request]
[2023-03-27 07:17:54.841][main-1][0ms] Total time taken: 605ms

BUILD SUCCESSFUL in 1s
2 actionable tasks: 1 executed, 1 up-to-date

Children
  1. Example