launch — essence, fire-and-forget coroutine launch and how it works

Author: IT Sectr Published: 2026-06-22 Reading time: 7 min

launch — a Coroutine Builder in Kotlin that starts a coroutine without returning a result, returning a Job object to control execution. This builder implements the fire-and-forget pattern: the coroutine starts working in a background thread and completes automatically. According to JetBrains documentation, 2024, launch is the primary way to run parallel tasks in Kotlin Coroutines.

Key Takeaways

  • launch — a builder for starting coroutines without returning a result (fire-and-forget)
  • Job — a coroutine control object with cancel(), join(), isActive methods
  • launch integrates with CoroutineScope for automatic cancellation of child coroutines
  • CoroutineStart.LAZY allows deferring execution until explicit start() or join() call
  • launch in Android is used for database saves, network calls, and UI updates

What is launch in Kotlin Coroutines?

launch is a Kotlin extension function, available through the kotlinx.coroutines import, that creates a new coroutine in a given CoroutineScope. Unlike regular functions, code inside launch executes concurrently — the coroutine can suspend without blocking a thread and resume later.

The launch builder is a fundamental building block of the Kotlin coroutine model. It does not return a computed value but returns a Job object that allows controlling the coroutine execution. All child coroutines launched via launch inside another coroutine are bound to the parent.

Syntax and parameters of launch

The launch builder is defined as an inline function with several parameters: CoroutineScope, CoroutineContext, CoroutineStart, and a suspend block. The parameters have default values, making launch convenient for typical scenarios.

kotlin
public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
): Job

The context parameter accepts a CoroutineContext that combines a dispatcher, exception handler, and a named element. Most often Dispatchers.IO, Dispatchers.Main, or SupervisorJob are passed for error isolation.

Job and lifecycle management

Job — the returned object representing the coroutine lifecycle. A Job can be in states: New, Active, Completing, Completed, Cancelling, Cancelled. Each state reflects the execution status and allows reacting to changes.

kotlin
val job = CoroutineScope(Dispatchers.IO).launch {
    repeat(10) { i ->
        delay(1000L)
        println("Progress: $i")
    }
}

println("Job is active: ${job.isActive}")
delay(2500L)
job.cancel()
println("Job is cancelled: ${job.isCancelled}")

Job supports a hierarchy: if a parent coroutine is cancelled, all children are automatically cancelled. SupervisorJob changes this behavior — child coroutines are not cancelled when one of them fails.

Structured concurrency in launch

Structured concurrency means that each coroutine is launched within a specific CoroutineScope, and the scope will not complete until all child coroutines finish their work. launch fully supports this principle — coroutines launched inside another coroutine are its children.

Nested launch calls

With nested launch calls, a coroutine tree is formed. The parent coroutine waits for all children to complete, ensuring predictable execution order and simplifying resource management.

kotlin
fun main() = runBlocking {
    launch {
        launch {
            delay(1000L)
            println("Child 1 completed")
        }
        launch {
            delay(500L)
            println("Child 2 completed")
        }
        println("All children complete before this line")
    }
}

This behavior differs from thread-based concurrency, where child threads are not bound to the parent. In coroutines, the parent does not complete until all children finish their work.

Exception handling in launch

Exception handling in launch depends on the type of Job. For a regular Job, exceptions propagate to the parent coroutine and cancel it. For SupervisorJob or SupervisorScope, exceptions are isolated — an error in one child coroutine does not affect the others.

kotlin
val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
scope.launch {
    try {
        riskyOperation()
    } catch (e: Exception) {
        println("Caught: ${e.message}")
    }
}
scope.launch {
    println("This still runs thanks to SupervisorJob")
}

For global handling of uncaught exceptions, CoroutineExceptionHandler is used. It catches exceptions that were not handled inside the coroutine and allows logging the error without crashing the application.

Usage examples of launch in Android

In Android, launch is used everywhere: from ViewModel to WorkManager. The main pattern is using lifecycleScope in Fragment and viewModelScope in ViewModel for automatic coroutine cancellation when the component lifecycle ends.

kotlin
class ProfileViewModel : ViewModel() {
    private val repository = UserRepository()

    fun loadProfile(userId: String) {
        viewModelScope.launch(Dispatchers.IO) {
            val profile = repository.fetchProfile(userId)
            withContext(Dispatchers.Main) {
                _profileState.update { it.copy(profile = profile) }
            }
        }
    }
}

viewModelScope automatically cancels coroutines when the ViewModel is destroyed. This eliminates leaks and ensures that background operations do not continue after losing context. Additionally, withContext can be used to switch between dispatchers.

Frequently Asked Questions

How is launch different from async in Kotlin Coroutines?

launch returns a Job and does not return a result, while async returns Deferred<T> to obtain the result. launch is used for fire-and-forget operations, async — when you need to wait for and use the returned value.

How to cancel a coroutine launched via launch?

Call job.cancel() on the saved Job object. For group cancellation, cancel the entire CoroutineScope via scope.cancel(). The coroutine must be cooperative — check isActive or use cancellable suspend functions.

What if launch throws an exception?

Use try-catch inside the launch block or pass CoroutineExceptionHandler in the CoroutineContext. For error isolation, use SupervisorJob — then an exception in one child coroutine will not cancel the others.

Can launch be used without CoroutineScope?

No, launch is an extension function of CoroutineScope. Without a scope, the coroutine cannot follow the structured concurrency principle. Use GlobalScope.launch with caution — it creates a coroutine without lifecycle binding.

How many launch coroutines can run simultaneously?

The number is limited by the dispatcher thread pool. Dispatchers.Default uses as many threads as there are CPU cores. Dispatchers.IO supports up to 64 threads. If more coroutines are launched, they are queued.

Summary

  • launch — the main builder for fire-and-forget coroutines, returning a Job object
  • Job provides control methods: cancel(), join(), isActive, isCancelled, isCompleted
  • Structured concurrency guarantees cancellation of child coroutines when parent is cancelled
  • Exception handling depends on Job type: regular propagates errors, SupervisorJob isolates them
  • lifecycleScope and viewModelScope — ready-made solutions for launching coroutines in Android
  • CoroutineStart.LAZY defers execution until explicit start() or join() call
  • launch in Android is used for network requests, database writes, analytics, and caching

We will develop a mobile application turnkey

IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.

Discuss the project

Read also