Skip to content
Table of contents
Coroutine Builders

Coroutine Builders

1 min read #kotlin / #asynchronous / #coroutine

Coroutine Builders

There are 3 coroutine builders:

  1. launch
  2. async
  3. runBlocking

Basic usage of coroutine

Let’s take a look at a simple kotlin program with a coroutine builder.

fun main() {
    GlobalScope.launch {
        delay(500)
        println("printed from within Coroutine")
    }
    println("main end")
}
kotlin

This will just print out the “main end” without waiting for the other print statement inside coroutine block. If we want the message to appear, one way is to add Thread.sleep(>500).

fun main() {
    GlobalScope.launch {
        delay(500)
        println("printed from within Coroutine")
    }
    Thread.sleep(1000)
    println("main end")
}
kotlin
printed from within Coroutine
main ends
bash

runBlocking

The Thread.sleep(ms) is ugly, to avoid that, Coroutine has a builder for it called runBlocking. This will force the process to wait before it could end.

fun main() {
    runBlocking {
        launch {
            delay(500)
            println("printed from within Coroutine")
        }        
    }
}
kotlin

This could be expressed as

fun main() = runBlocking {
    launch {
        delay(500)
        println("printed from within Coroutine")      
    }
}
kotlin

This works okay if we do not plan to access the result from the coroutine inside launch. If we are expecting the result from coroutine, we could use the Job as Coroutine returns.

fun main() = runBlocking {
    launch {
        networkRequest()
        print("result received")
    }
    println("end of runBlocking")
}

suspend fun networkRequest(): String {
    delay(500)
    return "Result"
}
kotlin
end of runBlocking
result received
bash

When the above code runs, we noticed that launch is another coroutine and doesn’t block the print statement for “end of runBlocking”. If we want the launch coroutine print statement to appear first, then we may need to use Job.

fun main() = runBlocking {
    val job = launch {
        networkRequest()
        print("result received")
    }
    job.join()
    println("end of runBlocking")
}

suspend fun networkRequest(): String {
    delay(500)
    return "Result"
}
kotlin
result received
end of runBlocking
bash

launch with LAZY

Another way is to make the launch coroutine to LAZY type.

fun main() = runBlocking {
    val job = launch(start = CoroutineStart.LAZY) {
        networkRequest()
        print("result received")
    }
    // since it is LAZY, we could do other works
    delay(200)
    job.start()
    println("end of runBlocking")
}

suspend fun networkRequest(): String {
    delay(500)
    return "Result"
}
kotlin

In this case, we could choose to start the Job anytime.

Conclusion

In Kotlin, coroutine builders like launch, async, and runBlocking serve distinct purposes in managing asynchronous tasks.

While launch creates non-blocking coroutines, it doesn’t halt the calling code. runBlocking forces the program to wait for coroutine completion but might not be ideal for result-dependent tasks.

Handling coroutine results is achieved through the returned Job from launch. Utilizing job.join() ensures specific order of execution.

The CoroutineStart.LAZY option with launch enables controlled coroutine start, allowing the program to perform other tasks before explicitly initiating the coroutine with job.start().

Understanding these builders provides flexibility in managing asynchronous operations in Kotlin, ensuring effective handling of concurrent tasks while maintaining code clarity.

Next topic, we will look at Coroutine that runs sequentially and concurrently as well as the difference between launch and async.