Kotlin suspend Functions
A suspend function is a function that can pause its execution at a specific point and resume later without blocking the thread. The suspend keyword marks such a function. You can only call suspend functions from inside a coroutine or another suspend function.
Defining a suspend Function
import kotlinx.coroutines.*
suspend fun fetchUserName(userId: Int): String {
delay(500) // simulate network delay (non-blocking)
return "User_$userId"
}
suspend fun fetchUserAge(userId: Int): Int {
delay(300)
return 20 + userId
}
fun main() = runBlocking {
val name = fetchUserName(42)
val age = fetchUserAge(42)
println("$name, age $age")
}Output:
User_42, age 62Why suspend is Needed
Normal function:
fun fetchData(): String {
Thread.sleep(1000) ← BLOCKS the entire thread
return "data"
}
suspend function:
suspend fun fetchData(): String {
delay(1000) ← PAUSES the coroutine only; thread stays free
return "data"
}
Calling Rules
suspend fun A() { ... }
✓ Can call A() from another suspend fun:
suspend fun B() { A() } // OK
✓ Can call A() from a coroutine builder:
runBlocking { A() } // OK
launch { A() } // OK
async { A() } // OK
✗ Cannot call A() from a normal function:
fun normalFun() { A() } // ERROR: Suspend function called from non-coroutine context
Sequential vs Concurrent suspend Calls
import kotlinx.coroutines.*
suspend fun loadConfig(): String {
delay(400); return "config_loaded"
}
suspend fun loadUser(): String {
delay(600); return "user_loaded"
}
fun main() = runBlocking {
val start = System.currentTimeMillis()
// Sequential: total ~1000ms
val config = loadConfig()
val user = loadUser()
println("Sequential: $config, $user | time=${System.currentTimeMillis()-start}ms")
}
// To run concurrently, use async (covered in coroutine builders topic)suspend with Return Values
data class WeatherData(val city: String, val tempC: Double, val condition: String)
suspend fun getWeather(city: String): WeatherData {
delay(800) // simulate API call
return WeatherData(city, 28.5, "Partly Cloudy")
}
fun main() = runBlocking {
val weather = getWeather("Delhi")
println("${weather.city}: ${weather.tempC}°C, ${weather.condition}")
}Output:
Delhi: 28.5°C, Partly CloudyException Handling in suspend Functions
suspend fun riskyOperation(): String {
delay(200)
if (Math.random() < 0.5) throw RuntimeException("Service unavailable")
return "Success"
}
fun main() = runBlocking {
try {
val result = riskyOperation()
println("Result: $result")
} catch (e: RuntimeException) {
println("Error: ${e.message}")
}
}suspend vs Regular Function: At a Glance
Normal Function suspend Function
────────────────────────────────────────────────────────
Can pause? NO YES
Blocks thread? YES (if blocking) NO
Can call delay()? NO YES
Call from anywhere? YES Only coroutines
Returns result? YES YES
Practical Example: Multi-Step Profile Loader
import kotlinx.coroutines.*
suspend fun loadAvatar(userId: Int): String {
delay(300)
return "avatar_$userId.png"
}
suspend fun loadPosts(userId: Int): List {
delay(500)
return listOf("Post 1 by $userId", "Post 2 by $userId")
}
suspend fun buildProfile(userId: Int): String {
val avatar = loadAvatar(userId)
val posts = loadPosts(userId)
return """
User: $userId
Avatar: $avatar
Posts: ${posts.joinToString(", ")}
""".trimIndent()
}
fun main() = runBlocking {
val profile = buildProfile(101)
println(profile)
} Output:
User: 101
Avatar: avatar_101.png
Posts: Post 1 by 101, Post 2 by 101