Ximon
Ximon

Reputation: 143

Kotlin await not suspending?

i have the following code in Kotlin

import kotlinx.coroutines.experimental.async
import kotlinx.coroutines.experimental.delay
import kotlinx.coroutines.experimental.runBlocking

fun main(args: Array<String>) {

    runBlocking {
        val async1 = async {
            println("1st")
            delay(2000)
            println("1st end")
        }
        val async2 = async {
            println("2nd")
            delay(1000)
            println("2nd end")
        }

        async1.await()
        async2.await()

        println("end")
    }
}

The output is

1st
2nd
2nd end
1st end
end

My understanding was, that await() is a suspending function, meaning the execution is "paused" there. So i thought, that actually first async1 would be executed, and THEN async2 would be executed. So i expected the output to be

1st
1st end
2nd
2nd end    
end

What obviously happened was, that both async1 and async2 where executed in parallel, which can be seen as the output of async1 is sandwiched between the outputs of async2.

So my concrete question is now: Why did Kotlin not suspend on async1, but concurrently started async2?

Upvotes: 1

Views: 2436

Answers (2)

Marko Topolnik
Marko Topolnik

Reputation: 200148

Why did Kotlin not suspend on async1, but concurrently started async2?

async spawns a new task in the background. The task starts executing right away. You have no control over its suspension and there's a whole class of use cases where it never gets suspended. We often use async to submit blocking tasks to a thread pool.

await suspends the current coroutine until the task has completed and produced its result. It has no effect on the execution of the task. The task will run to completion even if you never call await.

If you specifically want to fire the task only when you call await, you can specify start = LAZY:

runBlocking {
    val async1 = async(start = LAZY) {
        println("1st")
        delay(2000)
        println("1st end")
    }
    val async2 = async(start = LAZY) {
        println("2nd")
        delay(1000)
        println("2nd end")
    }

    async1.await()
    async2.await()

    println("end")
}

This reliably prints in the order you expected.

Upvotes: 2

DmitryBorodin
DmitryBorodin

Reputation: 4952

await() will suspend coroutine it run's in - which is your main block (and a thread runBlocking() is running, because it's blocking), so async2.await() will not be called, unless async1 is finished. But since execution of async() function is started immediately and will be executed by default on background thread pool, it will not be blocked. You can check it by adding

 async1.await()
 println("between")
 async2.await()

and see, that "between" will always be printed after "1st end"

If you want coroutines to run sequentially - use runBlocking() builder (or in most cases no need to use coroutines at all for such cases)

The idea of async/await is to run tasks in parallel and don't block other tasks while you waiting for results of one of them.

Upvotes: 2

Related Questions