What does suspend mean?

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

Pick the fix

This function is called from the main thread and the UI freezes. What is the right fix?

suspend fun countPrimes(limit: Int): Int {
    var count = 0
    for (n in 2..limit) {
        if ((2..Math.sqrt(n.toDouble()).toInt()).none { n % it == 0 }) count++
    }
    return count
}
Options
Hint 1 of 2

Which thread is the loop running on?

Hint 2 of 2

Who should decide the dispatcher: the caller or the function?

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

Show answer

Answer: A Wrap the body in withContext(Dispatchers.Default) { ... }

Correct. The function becomes main-safe: it moves its own CPU work off the main thread.

Why not the others?
  • B Nothing; suspend already runs it in the background. suspend doesn't change threads. The loop runs on the caller's main thread.
  • C Add delay(1) at the start. That suspends once, then resumes on main and runs the loop there anyway.
  • D Call it with GlobalScope.async. That leaks the work outside any lifecycle and doesn't fix the function itself.

30-second answer

suspend marks a function that may pause without blocking its thread and resume later. It can only be called from a coroutine or another suspend function. It does not move work to a background thread: a suspend function that does CPU work runs on whatever dispatcher called it.

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

Say what suspend does and what it doesn't.

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

  • It can pause without blocking the thread
  • Callable only from coroutines or suspend functions
  • It doesn't change threads
  • Use withContext for main-safety

Deep dive

The compiler rewrites a suspend function into a state machine that takes a hidden Continuation parameter. Each suspension point is a state.

A suspend function only actually suspends when it calls something that suspends, such as delay, withContext or a suspending library call.

Main-safety comes from the function choosing its own dispatcher with withContext, not from the keyword.

Callback APIs can be wrapped with suspendCancellableCoroutine, resuming the continuation exactly once.

Wrapping a callback API Compile-checked · Kotlin 2.4.21
suspend fun awaitLocation(client: LocationClient): Location =
    suspendCancellableCoroutine { cont ->
        val callback = object : LocationCallback {
            override fun onResult(location: Location) { cont.resume(location) }
            override fun onError(e: Exception) { cont.resumeWithException(e) }
        }
        client.request(callback)
        cont.invokeOnCancellation { client.cancel(callback) }
    }

Common mistakes

  • Believing suspend runs the body on a background thread.
  • Marking functions suspend that never suspend, which spreads the keyword for no reason.
  • Using suspendCoroutine instead of suspendCancellableCoroutine, so cancellation never reaches the callback API.

Senior take & follow-ups

Seniors connect the keyword to the compiler's continuation-passing transform and say clearly that threading is decided by dispatchers.

Follow-up questions

Think of your answer first, then open the hint.

  1. How do you wrap a one-shot callback API?

    Show hint

    suspendCancellableCoroutine, resume once, and unregister in invokeOnCancellation.

  2. Why can't a regular function call a suspend function?

    Show hint

    It has no continuation to pass, so there is nothing to resume.

Similar on iOS: async/await vs completion handlers Swift's async is a language feature with the same idea; Kotlin's suspend is a compiler transform plus a library.

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