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 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.
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.
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 — 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.
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 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.
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.
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 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.
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.
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.
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
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.
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.
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.
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.
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
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.
Read also