liveBook · Manning

Coroutine scope

As explained in Kotlin in Action, 2E

coroutineScope is a kotlinx.coroutines suspending function that creates a nested coroutine scope, waits for its child coroutines, and returns the block's result.

A coroutine scope gives coroutines a structured lifetime. Coroutines launched from a scope become part of its parent-child hierarchy, so the parent can track its work rather than requiring manual bookkeeping or awaits. This structure also supports cancellation of child work when its parent is cancelled. section 15.1 section 15.3

Use the suspending coroutineScope function to split one computation into concurrent pieces. It creates a nested scope, waits until all children complete, and returns the value produced by its block. Because it suspends, a caller such as computeSum must also be suspending. section 15.1.1 section 15.3

import kotlinx.coroutines.*
import kotlin.random.Random
import kotlin.time.Duration.Companion.milliseconds
 
suspend fun generateValue(): Int {
    delay(500.milliseconds)
    return Random.nextInt(0, 10)
}
 
suspend fun computeSum() {                 #1
    log("Computing a sum...")
    val sum = coroutineScope {             #2
        val a = async { generateValue() }
        val b = async { generateValue() }
        a.await() + b.await()              #3
    }
    log("Sum is $sum")
}
 
fun main() = runBlocking {
    computeSum()
}
 
// 0 [main @coroutine#1] Computing a sum...
// 532 [main @coroutine#1] Sum is 10

Two async children generate values concurrently; coroutineScope waits for both await calls and yields their sum before execution continues. section 15.1.1

Choose scopes by the lifetime and kind of work involved:

  • Use coroutineScope for bounded concurrent work whose combined result is needed before the enclosing operation can continue. section 15.1.1
  • Use the CoroutineScope constructor for a class-owned lifetime, then cancel that scope when the component stops; practical scopes commonly include a SupervisorJob. section 15.1.2
  • Prefer an existing framework scope when its lifetime matches the work, such as viewModelScope for a screen or a Ktor request scope for request-bound work. section 15.2.9
  • Avoid GlobalScope for ordinary application work because it breaks the parent hierarchy and can let work outlive its intended owner. section 15.1.3

Returning a concurrent result

A suspend function can use coroutineScope to start two async image loads, await both values, and return combineImages(...) as one structured operation. If the work belongs to a request instead, launching from that request's scope makes it cancel when the client disconnects. section 1.2.4 section 15.2.9

CoroutineScope and coroutineScope have similar names but different jobs. CoroutineScope creates a standalone scope immediately for a managed lifecycle; coroutineScope is a suspending nested scope for decomposing and completing a computation. Scope choice determines whether work ends with a component, request, application, or enclosing coroutine. section 15.1.2 section 15.2.9

How Kotlin in Action, 2E covers it

How deep it goes
Distinguishes nested coroutineScope from standalone CoroutineScope and relates both to lifecycle cancellation without detailing context internals.
Examples
Uses concurrent number generation, image loading, a component-owned scope, Android ViewModel, and Ktor request handling.
What it assumes
Builds from coroutine builders and suspend functions, then introduces scope ownership and child waiting.
What you can do after
Apply it via kotlinx.coroutines to bound concurrent work or attach coroutines to an appropriate lifecycle.
How it connects
Connects structured concurrency with Job, async, cancellation, SupervisorJob, coroutine context, and GlobalScope.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Kotlin in Action, 2E. The section links point to the parts of the book it draws on.