What does the suspending function coroutineScope do, and how is it different from launch or runBlocking?
answer
- suspend, not block
- waits for ALL children
- new Job, inherited context
- returns block result
- structured concurrency boundary
basics
~10 scoroutineScope starts a block where you can run several coroutines and it waits for all of them to finish before returning. It does not block the thread; it only suspends.
solid answer
~40 scoroutineScope is a suspend function that creates a new scope, runs the given block, and suspends until every child coroutine launched inside it completes. Unlike launch (which is fire-and-forget and returns a Job immediately) or async (which returns a Deferred), coroutineScope returns the block's own result and only resumes the caller once all children are done. Unlike runBlocking, it does not block the calling thread — it suspends, so it must be called from another suspend function or coroutine. It inherits the parent's CoroutineContext (dispatcher, etc.) but installs a fresh Job, making it a structured-concurrency boundary: if any child fails, the scope cancels the rest and rethrows.
code
kotlin · 8 linessuspend fun main() {
val result = coroutineScope {
val a = async { 1 }
val b = async { 2 }
a.await() + b.await()
}
println(result) // 3, printed only after both finish
}go deeper
Knows it runs a block, waits for all children, and suspends rather than blocks.
Contrasts it cleanly with launch/async/runBlocking/withContext and mentions inherited context plus new Job.
Frames it as a structured-concurrency boundary and explains failure propagation (one failure cancels siblings).
Discusses where to place such boundaries in an API for cancellation safety and how it composes with supervisorScope and custom CoroutineScopes.
## What coroutineScope is `coroutineScope { ... }` is a **suspending function** (you can only call it from a coroutine or another `suspend` function). It creates a new `CoroutineScope`, runs the lambda you pass, and **suspends the caller until all coroutines started inside the block have completed**. Its return value is whatever the block returns. ## Structured concurrency boundary The key idea is *structured concurrency*: child coroutines have a defined lifetime tied to their scope. `coroutineScope`: - **Inherits** the parent `CoroutineContext` (dispatcher, `CoroutineName`, etc.). - Installs a **new `Job`** as the parent of all children launched inside it. - Does **not return** until every child finishes (success, failure, or cancellation). ```kotlin suspend fun loadDashboard(): Dashboard = coroutineScope { val user = async { fetchUser() } // child 1 val stats = async { fetchStats() } // child 2 Dashboard(user.await(), stats.await()) // block result } ``` `loadDashboard` only resumes after both `async` children complete. ## How it differs from the builders - **`launch`**: a coroutine builder that returns a `Job` immediately and runs fire-and-forget. It does *not* wait. `coroutineScope` is not a builder you forget — it *awaits* its children. - **`async`**: returns a `Deferred<T>`; you must `await()` it. `coroutineScope` returns the plain block result directly. - **`runBlocking`**: **blocks** the current thread until completion and is meant as a bridge from non-coroutine code (e.g. `main`, tests). `coroutineScope` **suspends** instead of blocking, so it never ties up a thread. - **`withContext`**: switches the `CoroutineContext` (e.g. dispatcher) for the block; `coroutineScope` keeps the same context but adds a Job boundary. ## Failure semantics With `coroutineScope`, if **any** child fails, the scope cancels the remaining children and rethrows the exception from `coroutineScope` itself. This is the contrast with `supervisorScope`, where children fail independently.
- Can you call coroutineScope from a regular (non-suspend) function?No. It is a suspend function, so it must be called from a coroutine or another suspend function. From plain code you'd use runBlocking or a CoroutineScope.launch instead.
- Does coroutineScope create a new thread?No. It inherits the parent's dispatcher and runs on whatever thread(s) that dispatcher provides; it only adds a Job, not a thread.
Like a foreman who hands out tasks and won't leave the site until every worker has finished.
saying these in an interview costs you the question
- Saying it blocks the thread like runBlocking
- Thinking it returns immediately like launch
- Claiming it creates a new dispatcher or thread
- Not knowing it waits for all children