What's Behind the suspend Keyword in Kotlin?
If you've ever written code in Kotlin and tried to call a function marked with the suspend«What does a suspend function mean in Kotlin coroutines?» — has garnered thousands of views. Let's figure it out once and for all.
A Suspend Function Is Not Magic, It's a Contract
Imagine you're ordering a pizza. You call the restaurant, place your order, and instead of waiting on the phone for 20 minutes, you continue with your own tasks. When the pizza is ready, they call you — you «return» to the conversation. This is what a suspend function does: it can pause its execution without blocking the thread, and then resume when the result is available.
Technically, suspend is not a keyword that makes a function asynchronous «out of nowhere». It's a contract: the function can suspend, but it doesn't have to. The Kotlin compiler transforms such a function into a state machine that can save and restore its state.
What Does a Suspend Function Look Like in Code?
Here's a simple example:
suspend fun fetchUserData(userId: String): User { delay(1000) // simulating a network request return User(userId, "Alexey")}You cannot call fetchUserData just like that, outside a coroutine:
// Compilation error!fun main() { val user = fetchUserData("123") // Suspend function 'fetchUserData' should be called only from a coroutine or another suspend function}To call it, you need to be inside a coroutine. For example:
fun main() = runBlocking { val user = fetchUserData("123") println(user.name) // Alexey}runBlocking is a bridge between the regular world and the world of coroutines. In real projects, you'll almost never use it in production (except for tests and main). Instead, you'll launch coroutines through CoroutineScope.
What Happens «Under the Hood»?
When the compiler sees suspend, it generates an additional parameter — a Continuation object. This object stores the point at which the coroutine was suspended, along with all local variables. Thanks to this, the coroutine can «sleep» in one thread and «wake up» in another — without blocking.
Important: A suspend function does not block the thread. While it's «sleeping» (for example, waiting for a server response), the thread can execute other tasks. This is a key difference from older approaches (Thread.sleep, callback hell).
Best Practices for Using Suspend Functions in 2025
In recent years, the Kotlin ecosystem has grown significantly. New libraries have appeared, and the compiler and IDE have improved. Here's what you should know and apply right now.
1. Don't Make Suspend Functions That Don't Suspend
If a function doesn't contain calls to other suspend functions (e.g., delay, withContext, API calls via Ktor/Retrofit), don't mark it with suspend. This is misleading and adds unnecessary overhead.
// Badsuspend fun formatName(first: String, last: String): String { return "$first $last".trim()}
// Goodfun formatName(first: String, last: String): String { return "$first $last".trim()}2. Use withContext to Switch Dispatchers
If you need to perform a blocking operation (reading a file, working with a database), don't mark the function as suspend and don't make it