Suspension is not automatic parallelism
A suspending function may pause without blocking its thread, but suspend alone neither creates a coroutine nor moves work off the current dispatcher. Builders and scopes establish the coroutine's lifetime.
The following JVM example requires kotlinx-coroutines-core in your learning project's dependencies.
import kotlinx.coroutines.*
suspend fun combined(): Int = coroutineScope {
val first = async { delay(20); 10 }
val second = async { delay(10); 15 }
first.await() + second.await()
}
fun main() = runBlocking { check(combined() == 25) }
coroutineScope waits for its children. async returns a Deferred; await obtains its result. launch returns a Job for work with no result. runBlocking bridges a console entry point and blocks its thread; it is unsuitable for blocking an Android UI thread.
Exercise
Replace the concurrent children with sequential suspending calls and compare the structure. Cancel the enclosing job and observe child cleanup.
Check: explain who owns each coroutine and when its parent can finish.