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 62

Why 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 Cloudy

Exception 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

Leave a Comment

Your email address will not be published. Required fields are marked *