Coroutine scope and structured concurrency in Kotlin - Time & Space Complexity
We want to understand how the time cost grows when using coroutine scopes and structured concurrency in Kotlin.
Specifically, how launching and managing coroutines affects the total work done as input size changes.
Analyze the time complexity of the following code snippet.
fun fetchAllData(ids: List<Int>) = runBlocking {
ids.forEach { id ->
launch {
fetchData(id) // suspending function
}
}
}
suspend fun fetchData(id: Int) {
delay(100) // simulate network call
}
This code launches one coroutine per id to fetch data concurrently inside a coroutine scope.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Launching a coroutine for each id in the list.
- How many times: Once per element in the input list (n times).
Each id causes one coroutine to start, so the total number of coroutines grows linearly with input size.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 10 coroutines launched |
| 100 | 100 coroutines launched |
| 1000 | 1000 coroutines launched |
Pattern observation: The work grows directly in proportion to the number of input items.
Time Complexity: O(n)
This means the total time grows linearly as you add more items to process concurrently.
[X] Wrong: "Launching many coroutines means the program runs instantly regardless of input size."
[OK] Correct: Even though coroutines run concurrently, each one still does work, so total time and resource use grow with the number of coroutines.
Understanding how coroutine scopes manage concurrent tasks helps you explain how programs handle many jobs at once without losing control.
"What if we changed launch to async and awaited all results? How would the time complexity change?"
Practice
CoroutineScope in Kotlin?Solution
Step 1: Understand CoroutineScope role
ACoroutineScopegroups coroutines so they can be managed as one unit.Step 2: Compare other options
The other options describe unrelated or incorrect behaviors forCoroutineScope.Final Answer:
To group coroutines and manage their lifecycle together -> Option CQuick Check:
CoroutineScope groups coroutines [OK]
- Thinking CoroutineScope creates threads
- Confusing CoroutineScope with blocking calls
- Assuming CoroutineScope converts coroutines to sync
CoroutineScope?Solution
Step 1: Recall coroutine launch syntax
Within aCoroutineScope, you calllaunchas a function on the scope instance.Step 2: Check options for syntax correctness
scope.launch { /* coroutine code */ } usesscope.launch {}which is correct. Others misuse syntax or functions.Final Answer:
scope.launch { /* coroutine code */ } -> Option AQuick Check:
launch is called on CoroutineScope instance [OK]
- Swapping order like launch.scope
- Using run instead of launch
- Calling launch as a static method
import kotlinx.coroutines.*
fun main() = runBlocking {
val job = launch {
delay(100)
println("Task done")
}
println("Waiting")
job.join()
println("Finished")
}Solution
Step 1: Analyze coroutine launch and delay
The coroutine launched delays 100ms before printing "Task done". Meanwhile, "Waiting" prints immediately.Step 2: Understand job.join() effect
The main coroutine waits for the launched job to finish before printing "Finished".Final Answer:
Waiting\nTask done\nFinished -> Option BQuick Check:
Output order respects delay and join [OK]
- Assuming launched coroutine runs instantly
- Ignoring delay effect
- Printing Finished before job completes
fun main() = runBlocking {
val job = GlobalScope.launch {
delay(1000)
println("Done")
}
println("Waiting")
}Solution
Step 1: Identify lifecycle issue with GlobalScope
UsingGlobalScope.launchstarts coroutine that may outliverunBlocking, so "Done" may never print.Step 2: Fix by waiting for job completion
Callingjob.join()ensures main coroutine waits for launched coroutine to finish before exiting.Final Answer:
Add job.join() after println("Waiting") -> Option DQuick Check:
join() waits for coroutine to complete [OK]
- Ignoring job.join() causing premature exit
- Replacing delay with blocking sleep
- Removing runBlocking causing main to exit early
import kotlinx.coroutines.*
fun main() = runBlocking {
try {
coroutineScope {
launch {
delay(100)
println("Child 1 done")
}
launch {
delay(50)
throw RuntimeException("Error in Child 2")
}
}
} catch (e: Exception) {
println("Caught: " + e.message)
}
println("End of main")
}Solution
Step 1: Understand coroutineScope behavior on exceptions
If any child coroutine throws an exception,coroutineScopecancels all children and throws the exception.Step 2: Analyze timing and cancellation
Child 2 throws after 50ms, cancelling Child 1 before it prints. The exception is caught and message printed.Final Answer:
Caught: Error in Child 2\nEnd of main -> Option AQuick Check:
Exception cancels siblings, caught prints message [OK]
- Assuming Child 1 prints despite cancellation
- Missing try-catch around coroutineScope
- Thinking exceptions are ignored in coroutineScope
Ready to prove it?
Pass the test and this pattern moves unknown
