Suspending functions and coroutine basics
Suspension is not blocking
suspend fun loadUser(id: Long): User {
return api.user(id)
}
suspend means the function may pause and resume. While it is suspended waiting for non-blocking I/O, the thread may run other work. It does not automatically move work to a background thread.
Launch versus async
coroutineScope {
launch { analytics.refresh() } // result is Job
val user = async { api.user(42) } // result is Deferred<User>
render(user.await())
}
launch: work whose useful result is completion; failures are propagated through its scope.async: produces a value that is retrieved withawait().
Do not use async merely to call await() immediately; that adds complexity without concurrency.
Sequential versus concurrent
val user = api.user(id)
val orders = api.orders(id)
This is sequential. If operations are independent:
coroutineScope {
val user = async { api.user(id) }
val orders = async { api.orders(id) }
Dashboard(user.await(), orders.await())
}
Both children are owned by coroutineScope. If one fails, the scope cancels the other and does not return until its children complete.
✕ Blocking inside a coroutine
suspend fun load() = Thread.sleep(2_000)
✓ Suspend for delay or use an appropriate dispatcher for blocking APIs
suspend fun waitWithoutBlocking() = delay(2_000)
suspend fun readBlockingFile() = withContext(Dispatchers.IO) { file.readText() }
These snippets require kotlinx.coroutines; Android code also needs lifecycle-aware scopes. They are not linked to the dependency-free Playground configuration used by this site.
A suspending function may pause without blocking its thread, but suspend does not choose a dispatcher. launch returns a Job; async returns a Deferred result. Use concurrency only for independent work and keep children inside an owning scope.