What does the coroutineScope { } builder do, and how is it different from launching with GlobalScope?
answer
- coroutineScope suspends until all children finish
- Inherits caller context, adds a new parent Job
- Child failure cancels siblings + rethrows
- GlobalScope = app-lifetime, no owner, leak risk
- Prefer coroutineScope or a lifecycle-bound scope
basics
~20 scoroutineScope runs a block, starts child coroutines inside it, and waits until all of them finish before returning. GlobalScope launches coroutines that are not tied to anything and can outlive your code, so they can leak.
solid answer
~40 s`coroutineScope` is a suspending function that creates a child scope, runs its block, and **suspends until every coroutine started inside it completes**. It inherits the caller's `CoroutineContext` (dispatcher, etc.) and adds a new Job as parent. This gives structured concurrency: when `coroutineScope` returns, no work is still running, and if a child fails the scope cancels the rest and rethrows. `GlobalScope`, by contrast, is a rootless scope tied to the whole application lifetime: coroutines launched there have no parent to wait for them or cancel them, so they can outlive the surrounding function and leak. Idiomatic Kotlin avoids `GlobalScope` (it is marked `@DelicateApi`) and instead uses `coroutineScope`, a lifecycle-bound scope (e.g. `viewModelScope`), or passes a `CoroutineScope`.
go deeper
Knows coroutineScope waits for children and that GlobalScope can leak — the core takeaway.
Explains context inheritance, the new parent Job, and the suspend (non-blocking) nature.
Contrasts with withContext and lifecycle scopes, and articulates failure propagation precisely.
Sets team conventions banning GlobalScope and prescribing owned/lifecycle scopes across the codebase.
## coroutineScope in one sentence `coroutineScope { ... }` is a **suspending function** that creates a new scope bound to the calling coroutine, runs the block, and **does not return until all children launched inside have finished**. ## What it gives you - **Inherits context**: it uses the caller's `CoroutineContext` (dispatcher, name) and installs a fresh `Job` as the parent of inner coroutines. - **Joins children**: `launch`/`async` started inside are awaited automatically — you don't manage join() by hand. - **Structured failure**: if a child throws, siblings are cancelled and the exception is rethrown from the `coroutineScope` call. ```kotlin suspend fun fetchAll(): List<String> = coroutineScope { val a = async { fetchA() } val b = async { fetchB() } listOf(a.await(), b.await()) // both done before we return } ``` ## GlobalScope — the rootless escape hatch `GlobalScope` is a built-in `CoroutineScope` whose Job lives for the **entire application**. Coroutines launched there: - have **no structural parent** to wait on or cancel them, - can **outlive** the function/component that started them, - are easy to **leak** (keep running, holding resources, after the screen/request is gone). It is annotated `@DelicateApi` precisely because misuse breaks structured concurrency. ```kotlin // Anti-pattern: nobody owns or cancels this GlobalScope.launch { doForever() } ``` ## What to use instead - `coroutineScope { }` inside a `suspend` function for fan-out/fan-in. - A lifecycle-bound scope: `viewModelScope`, `lifecycleScope`, or a custom `CoroutineScope(SupervisorJob() + Dispatchers.Default)` you cancel on teardown. - Pass a `CoroutineScope` to the function so the caller owns the lifetime. ## Related builder: withContext vs coroutineScope `withContext(ctx) { }` also runs a block and waits, but its purpose is to **switch context/dispatcher**. `coroutineScope` keeps the same context and exists to **group children for structured concurrency**. ## Summary - coroutineScope = scoped, waits for children, propagates failure, inherits context. - GlobalScope = app-lifetime, no parent, leak risk, delicate API — avoid.
- Is coroutineScope blocking or suspending?Suspending — it must be called from a coroutine or another suspend function and it suspends (does not block the thread) until children complete.
- Why is GlobalScope marked @DelicateApi?Because launching there detaches work from any lifecycle, so it can leak and break structured concurrency; you must justify and manage it carefully.
coroutineScope is a meeting room you can't leave until everyone is done; GlobalScope is sending people off into the world with no one tracking when they come back.
saying these in an interview costs you the question
- Saying coroutineScope blocks the thread
- Claiming GlobalScope coroutines are automatically cancelled with the caller
- Confusing coroutineScope with runBlocking
- Thinking coroutineScope changes the dispatcher (that's withContext)
- Recommending GlobalScope for normal app work