Skip to main content

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 with await().

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() }
Dependency-specific code

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.

Interview-ready answer

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.