skip to content

What is `suspend fun main` and when would you use it?

level: middleimportance: should knowfreq 55%

answer

  1. suspend main = call suspend fns without runBlocking
  2. Added in Kotlin 1.3
  3. Compiler generates a synthetic blocking main
  4. Still blocks the launching thread
  5. Great for scripts; use runBlocking/scope in real apps

basics

~20 s

suspend fun main lets the program's entry point call suspending coroutine functions directly, without manually creating a runBlocking block. The runtime starts a coroutine for you and waits until it finishes before the program exits.

solid answer

~40 s

Since Kotlin 1.3, you can mark the entry point `suspend fun main()` or `suspend fun main(args: Array<String>)`. This lets you call `suspend` functions (like `delay`) directly in `main` without wrapping them in `runBlocking { }`. Under the hood the compiler generates a synthetic non-suspend `main` that bootstraps a coroutine via an internal `runSuspend`-style helper and blocks the launching thread until the top-level coroutine completes — so the JVM still gets an ordinary `static main(String[])` to call. It's mainly a convenience for small programs, scripts, and examples. In production apps you usually still use an explicit `runBlocking` or a structured `CoroutineScope` so you control the dispatcher, exception handling, and scope. `suspend main` runs on the default single-threaded event loop unless you switch context.

code

kotlin · 11 lines
kotlin
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.delay

suspend fun main() = coroutineScope {
    launch {
        delay(500)
        println("world")
    }
    println("hello")
}

go deeper

for a junior

Knows suspend main lets you call delay/suspend functions without runBlocking.

for a middle

Explains the synthetic blocking launcher and that it still blocks the launching thread until completion.

for a senior

Weighs suspend main vs explicit runBlocking/scope for dispatcher, exception handling, and structured concurrency.

for a principal

Considers startup architecture, lifecycle ownership, and why production entry points usually centralize scope/dispatcher policy rather than rely on suspend main.

## The problem it solves A **suspending function** (`suspend fun`) can pause and resume without blocking a thread — but it can only be called from another `suspend` function or a coroutine builder. Normally, to call one from a plain `main`, you'd write: ```kotlin import kotlinx.coroutines.* fun main() = runBlocking { delay(1000) println("done") } ``` `runBlocking` bridges the non-suspending world (`main`) and the suspending world by blocking the current thread until its coroutine finishes. ## What `suspend main` does Since **Kotlin 1.3**, you can declare the entry point itself as suspending: ```kotlin import kotlinx.coroutines.delay suspend fun main() { println("start") delay(1000) // calling a suspend fun directly println("done") } ``` No `runBlocking` needed. You can call `suspend` functions like `delay` straight from `main`. ## How it works under the hood The JVM can only launch a plain `static main(String[])`. So the Kotlin compiler generates a **synthetic, non-suspend `main`** that: 1. Starts the top-level coroutine for your `suspend fun main` using an internal runner (a `runSuspend`/event-loop helper from the standard library — note this needs `kotlin-stdlib`, and for `delay` you also need `kotlinx-coroutines`). 2. Blocks the launching thread until that coroutine completes, then returns. So from the JVM's view it's still an ordinary blocking `main`. ## When to use it (and not) - **Use it** for scripts, demos, CLI tools, and small programs where coroutine setup boilerplate isn't worth it. - **Prefer explicit `runBlocking` / a `CoroutineScope`** in real applications, because you often need to pick a dispatcher (`Dispatchers.IO`), install a `CoroutineExceptionHandler`, or apply structured concurrency. `suspend main` gives you a bare event-loop context with little control. ## Gotchas - It blocks the launching thread just like `runBlocking` — it's not magic non-blocking startup. - All four signatures are valid: `fun main()`, `fun main(args)`, `suspend fun main()`, `suspend fun main(args)`. - Mixing a regular `main` and a `suspend main` in the same file is ambiguous and should be avoided. ## Key keywords/APIs - `suspend` — marks a function as suspendable. - `delay` — a suspend function that pauses without blocking. - `runBlocking` — bridges blocking and suspending worlds. - `CoroutineScope`, `Dispatchers`, `CoroutineExceptionHandler` — the control you give up with bare `suspend main`.

  • Does `suspend main` make program startup non-blocking?
    No. The generated launcher still blocks the launching thread until the top-level coroutine completes — just like `runBlocking`.
  • Why might you still prefer `runBlocking` in production?
    To explicitly choose a dispatcher, install exception handling, and apply structured concurrency — control that bare `suspend main` doesn't expose.

It's a built-in runBlocking doormat at the front door — you step from the blocking street straight into coroutine-land.

saying these in an interview costs you the question

  • Claiming `suspend main` runs asynchronously and returns immediately
  • Saying you can call any suspend function from a normal `fun main` without a builder
  • Not knowing it was added in Kotlin 1.3
  • Thinking `suspend main` requires no stdlib/coroutines support at all
  • Believing `suspend main` removes the need for a dispatcher entirely in complex apps

context