Choosing a dispatcher

Coroutines Junior Kotlin 2.0+ API 24+ Reviewed By Best Latest Apps

Pick the fix

This sorts a 2-million-item list. Which dispatcher should the function use?

suspend fun sortedScores(scores: List<Int>): List<Int> =
    withContext(/* which dispatcher? */ Dispatchers.Default) {
        scores.sortedDescending()
    }
Options
Hint 1 of 1

Is this waiting, or computing?

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

Show answer

Answer: A Dispatchers.Default

Correct. Sorting is pure CPU work, which belongs on the pool sized to the cores.

Why not the others?
  • B Dispatchers.IO. IO is for blocking waits. It works, but it is the wrong pool for CPU work.
  • C Dispatchers.Main. That freezes the UI for the whole sort.
  • D Dispatchers.Unconfined. It would keep running on the caller's thread, which may be main.

30-second answer

Main runs on the UI thread and is for touching views and state. Default is a pool sized to the CPU cores, for CPU-heavy work like parsing or sorting. IO shares threads with Default but allows many more at once (64 by default), because blocking file and network calls mostly wait. Unconfined is for special cases only.

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

Describe when to use each dispatcher.

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

  • Main for UI
  • Default for CPU work
  • IO for blocking I/O, more threads
  • Room and Retrofit are already main-safe

Deep dive

Dispatchers.Main.immediate runs straight away if you are already on the main thread, avoiding an extra frame of delay. viewModelScope uses it.

Room, Retrofit and DataStore suspend functions are already main-safe, so you don't wrap them in withContext(IO).

Dispatchers.IO.limitedParallelism(n) gives a view with its own cap, useful for a database or a rate-limited API.

Inject dispatchers instead of hard-coding them so tests can swap in a StandardTestDispatcher.

Common mistakes

  • Running CPU work on IO, which allows 64 threads to compete for a few cores.
  • Wrapping already-main-safe Room or Retrofit calls in withContext(Dispatchers.IO) everywhere.
  • Using Dispatchers.Unconfined in app code.

Senior take & follow-ups

Seniors say which libraries are already main-safe and that the function doing the work picks the dispatcher.

Follow-up questions

Think of your answer first, then open the hint.

  1. Why inject dispatchers?

    Show hint

    So tests can replace them with a test dispatcher and control time.

  2. What does limitedParallelism do?

    Show hint

    It creates a view of a dispatcher that runs at most n tasks at once.

Similar on iOS: Serial vs concurrent queues GCD exposes queues you pick by hand; Kotlin dispatchers are chosen per coroutine and usually injected.

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