skip to content

What does the `suspend` modifier do, and where can you call a `suspend` function from?

level: juniorimportance: must knowfreq 85%

answer

  1. suspend = pause without blocking the thread
  2. Callable only from a coroutine or another suspend fun
  3. Needs a continuation -> only exists inside coroutines
  4. May suspend, only at suspension points
  5. Goal: non-blocking waiting

basics

~10 s

suspend marks a function that can pause and resume later without blocking its thread. You can only call it from a coroutine or from another suspend function.

solid answer

~40 s

The `suspend` modifier marks a function as suspendable: at certain points it can pause execution and free the underlying thread, then resume later from where it left off. While suspended it holds no thread, so other work can run on that thread. Because suspension needs a coroutine machinery (a continuation), a `suspend` function can only be invoked from a coroutine builder such as `launch`/`async`/`runBlocking`, or from another `suspend` function. You cannot call it from ordinary (non-suspend) code — the compiler rejects it. Typical suspending operations are `delay(...)`, network/IO calls in coroutine-aware libraries, and channel/flow operators. The point of `suspend` is non-blocking waiting: instead of tying up a thread while waiting, the coroutine suspends and the thread is returned to the pool.

code

kotlin · 9 lines
kotlin
suspend fun fetchGreeting(): String {
    delay(100) // non-blocking suspension point
    return "hi"
}

fun main() = runBlocking {   // coroutine builder establishes a coroutine
    val msg = fetchGreeting() // legal: we're inside runBlocking
    println(msg)
}

go deeper

for a junior

Knows suspend means pause-without-blocking and that it's callable only from coroutines or other suspend functions.

for a middle

Explains suspension points, that suspend != threading, and that the function may not actually suspend.

for a senior

Articulates the continuation requirement behind the call-site rule and the non-blocking-throughput motivation.

for a principal

Frames suspend as the foundation for structured concurrency and thread-pool efficiency, and reasons about API design (which functions should be suspend).

## What `suspend` means The `suspend` keyword is a function modifier in Kotlin: `suspend fun loadUser(): User`. It tells the compiler that the function may **suspend** — pause partway through and resume later — **without blocking the thread** it was running on. - **Blocking** = a thread is stuck doing nothing useful (e.g. `Thread.sleep`), unavailable for other work. - **Suspending** = the coroutine pauses, the thread is released back to do other things, and the coroutine resumes later (possibly on a different thread) from exactly where it stopped. ## The call-site rule A `suspend` function can only be called from: 1. **A coroutine builder** — `launch { }`, `async { }`, `runBlocking { }`, or `withContext { }`. 2. **Another `suspend` function.** ```kotlin suspend fun loadUser(): User { /* ... */ } fun ordinary() { // loadUser() // COMPILE ERROR: suspend function called from non-coroutine code } fun caller(scope: CoroutineScope) { scope.launch { val u = loadUser() // OK: inside a coroutine builder } } suspend fun outer() { val u = loadUser() // OK: inside another suspend function } ``` Why the restriction? Suspension needs extra machinery — a hidden **continuation** the compiler threads through — that only exists inside a coroutine. Plain functions have no continuation, so the call is illegal. ## Suspension points A `suspend` function does not always actually suspend. It only **may** suspend, and only at **suspension points** — calls to other suspend functions like `delay()`. Between suspension points it runs like normal sequential code. If nothing inside actually suspends, it just runs straight through. ## Why it matters The whole value is **non-blocking waiting**: you write straight-line, sequential-looking code, but waiting (for IO, a timer, a network call) does not hold a thread hostage. That lets a small thread pool serve huge numbers of concurrent operations. ## Common APIs you call from suspend code - `delay(timeMillis)` — non-blocking pause. - `withContext(Dispatchers.IO) { }` — switch dispatcher. - `coroutineScope { }` / `supervisorScope { }` — structured concurrency. - Suspending library calls (Ktor client, Room, Retrofit's suspend functions).

  • Does marking a function `suspend` make it run on a background thread?
    No. `suspend` is about pausing/resuming, not threading. The dispatcher (e.g. `Dispatchers.IO`) decides the thread; a `suspend` function runs on whatever thread the caller's coroutine uses unless you switch with `withContext`.
  • Can `main` be a suspend function?
    Yes — `suspend fun main()` is supported; the Kotlin compiler wraps it so it runs inside a coroutine.

Like pausing a video game and freeing the TV for someone else, then resuming your exact save point later — versus standing frozen in front of the TV the whole time.

saying these in an interview costs you the question

  • Saying `suspend` automatically moves work to another thread
  • Claiming you can call a suspend function from any normal function
  • Confusing suspending with blocking the thread
  • Thinking a suspend function always suspends every time it's called

context