Loading several things in parallel

Coroutines Mid-level Kotlin 2.0+ API 24+ Reviewed By Best Latest Apps

Order the steps

Put the steps of a parallel all-or-nothing load in order.

  • Call await on all three
  • Start each call with async
  • Return the combined result
  • Open coroutineScope { } inside the suspend function
Hint 1 of 1

Starting comes before waiting.

Choose an answer, then open Show answer below to check it.

Show answer

Answer: D B A C

  1. Open coroutineScope { } inside the suspend function The scope makes sure nothing outlives the function.
  2. Start each call with async Each starts at once, so the calls overlap.
  3. Call await on all three Waiting comes after starting, so total time is the slowest call.
  4. Return the combined result If any call failed, the scope already cancelled the rest and threw.

30-second answer

Inside one suspend function, wrap the calls in coroutineScope and start each with async, then await all three. They run at the same time, so the screen waits for the slowest call. If any call fails, the others are cancelled and the error is thrown, which suits all-or-nothing screens. For independent sections, use supervisorScope and catch per call.

Practise saying it · 45 s. No recording, just a timer

Explain how you'd load three APIs in parallel.

Say your answer out loud, then check which of these points you covered:

  • coroutineScope plus async
  • Total time is the slowest call
  • Failure cancels the rest
  • supervisorScope for independent sections
  • Limit fan-out with a Semaphore

Deep dive

For a dynamic list, ids.map { async { api.item(it) } }.awaitAll().

To show sections as they arrive, launch each into the ViewModel scope and update separate state, or merge Flows.

Cap fan-out with a Semaphore(4) and withPermit, so 200 IDs don't open 200 connections.

Use a timeout on the whole group with withTimeout, not on each call, when the screen has one deadline.

Fan-out with a cap Compile-checked · Kotlin 2.4.21
suspend fun loadAll(ids: List<String>): List<User> = coroutineScope {
    val permits = Semaphore(4)
    ids.map { id ->
        async { permits.withPermit { api.user(id) } }
    }.awaitAll()
}

Common mistakes

  • Awaiting each async straight after starting it.
  • Using GlobalScope.async so the calls outlive the screen.
  • Letting one optional section's failure blank the whole screen.

Senior take & follow-ups

Separate the two product decisions: all-or-nothing (coroutineScope) vs independent sections (supervisorScope). That's what seniors get asked next.

Follow-up questions

Think of your answer first, then open the hint.

  1. How do you limit to 4 concurrent requests?

    Show hint

    A Semaphore(4) with withPermit around each call.

  2. How would you show partial results?

    Show hint

    Separate state per section, or supervisorScope with per-call try/catch.

Suggest a fix if something here is wrong or out of date.