Launch vs Async in Kotlin Coroutines

Authors
Launch vs Async in Kotlin Coroutines

In Kotlin Coroutines, launch and async are the two functions that we use to start a coroutine. We use launch when we do not need a result back, and we use async when we need a result back.

In this blog, we will learn about the Launch vs Async in Kotlin Coroutines. We will see how the Launch and Async differ from each other and when to use which one.

I am Amit Shekhar, Founder @ Outcome School, I have taught and mentored many developers, and their efforts landed them high-paying tech jobs, helped many tech companies in solving their unique problems, and created many open-source libraries being used by top companies. I am passionate about sharing knowledge through open-source, blogs, and videos.

I teach AI and Machine Learning, and Android at Outcome School.

Join Outcome School and get a high-paying tech job:

Let's get started.

What is the difference between launch and async?

We use launch to start a coroutine when we do not need a result back, and we use async to start a coroutine when we need a result back.

Both launch and async are the functions in Kotlin to start the Coroutines.

  • launch{}
  • async{}

The difference is that the launch{} returns a Job and does not carry any resulting value whereas the async{} returns an instance of Deferred<T>, which has an await() function that returns the result of the coroutine like we have future in Java in which we do future.get() to get the result.

In other words:

  • launch: fire and forget.
  • async: perform a task and return a result.

Let's take an example to learn launch vs async.

We can use the launch as below:

val job = GlobalScope.launch(Dispatchers.Default) {
    // do something and do not return result
}

It returns a job object which we can use to get a job's status or to cancel it.

In the above example of launch, we have to do something and NOT return the result back.

But when we need the result back, we need to use the async.

val deferredJob = GlobalScope.async(Dispatchers.Default) {
    // do something and return result, for example 10 as a result
    return@async 10
}
val result = deferredJob.await() // result = 10

Here, we get the result using the await().

In async also, we can use the Deferred job object to get a job's status or to cancel it.

Note: I have used GlobalScope for quick examples, we should avoid using it at all costs. In an Android project, we should use custom scopes based on our usecase such as lifecycleScope, viewModelScope and etc.

Another difference between launch and async is in terms of exception handling.

If any exception comes inside the launch block, it crashes the application if we have not handled it.

However, if any exception comes inside the async block, it is stored inside the resulting Deferred and is not delivered anywhere else, it will get silently dropped unless we handle it.

Let's understand this difference with code examples.

Suppose we have a function that does something and throws an exception:

private fun doSomethingAndThrowException() {
    throw Exception("Some Exception")
}

Now using it with the launch:

GlobalScope.launch {
    doSomethingAndThrowException()
}

It will CRASH the application as expected.

We can handle it as below:

GlobalScope.launch {
    try {
        doSomethingAndThrowException()
    } catch (e: Exception) {
        // handle exception
    }
}

A quick note for you

No matter which tech domain you work in, get familiar with these topics:

  • LLM
  • RAG
  • MCP
  • Agent
  • Fine-tuning
  • Quantization

We put it all together in one video:

AI Engineering Explained: LLM, RAG, MCP, Agent, Fine-Tuning, and Quantization

No need to stop reading - bookmark it and watch later when you get time. Future you will thank you.

Now, let's get back to the topic.

Now, the exception will come inside the catch block and we can handle it.

And now, using it with the async:

GlobalScope.async {
    doSomethingAndThrowException()
}

The application will NOT crash. The exception will get dropped silently.

Again, we can handle it as below:

GlobalScope.async {
    try {
        doSomethingAndThrowException()
    } catch (e: Exception) {
        // handle exception
    }
}

Now, the exception will come inside the catch block and we can handle it.

Let me tabulate the difference between launch and async.

LaunchAsync
Fire and forget.Perform a task and return a result.
launch{} returns a Job and does not carry any resulting value.async{} returns an instance of Deferred<T>, which has an await() function that returns the result of the coroutine.
If any exception comes inside the launch block, it crashes the application if we have not handled it.If any exception comes inside the async block, it is stored inside the resulting Deferred and is not delivered anywhere else, it will get silently dropped unless we handle it.

So, we have understood the difference between the launch function and the async function.

Watch the video format: Launch vs Async in Kotlin Coroutines

To master launch, async, structured concurrency, and exception handling hands-on along with the rest of the Android stack, check out the Android Program by Outcome School.

Stay updated: Subscribe to our newsletter to get our latest AI and Machine Learning blogs straight to your inbox.

Frequently Asked Questions

Does an exception inside async crash the application?

No. If an exception comes inside the async block, it is stored inside the resulting Deferred and is not delivered anywhere else. It gets silently dropped unless we handle it, for example with a try-catch inside the block. With launch, an unhandled exception crashes the application.

Can we cancel a coroutine started with async?

Yes. Just like the Job returned by launch, the Deferred job object returned by async can be used to get the job's status or to cancel it. So, with both of them, we can control the coroutine after starting it.

What is Deferred similar to in Java?

Deferred is like a future in Java. In Java, we call future.get() to get the result. In Kotlin, we call await() on the Deferred to get the result of the coroutine. launch does not have this, because it returns a Job that carries no resulting value.

Should we use GlobalScope to start coroutines?

No. We should avoid using GlobalScope at all costs. In an Android project, we should use custom scopes based on our use case, such as lifecycleScope and viewModelScope.

That's it for now.

Thanks

Amit Shekhar
Founder @ Outcome School

You can connect with me on:

Follow Outcome School on:

Read all of our high-quality blogs here.