kotlinbeginner
Suspend Functions — Basics and Patterns
Understand suspend functions: sequential vs concurrent, suspend composition, and callback wrapping.
kotlinPress ⌘/Ctrl + Shift + C to copy
import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis
// Basic suspend function
suspend fun fetchUser(id: String): Map<String, Any> {
delay(200) // simulate network
return mapOf("id" to id, "name" to "User-$id", "email" to "$id@test.com")
}
suspend fun fetchOrders(userId: String): List<String> {
delay(300) // simulate network
return listOf("ORD-1", "ORD-2", "ORD-3")
}
suspend fun fetchBalance(userId: String): Double {
delay(150)
return 1234.56
}
// Sequential composition
suspend fun getUserProfile(id: String): Map<String, Any> {
val user = fetchUser(id) // waits 200ms
val orders = fetchOrders(id) // waits 300ms
// Total: ~500ms (sequential)
return user + mapOf("orders" to orders)
}
// Concurrent composition
suspend fun getUserProfileFast(id: String): Map<String, Any> = coroutineScope {
val userDeferred = async { fetchUser(id) } // starts immediately
val ordersDeferred = async { fetchOrders(id) } // starts immediately
val balanceDeferred = async { fetchBalance(id) } // starts immediately
// Total: ~300ms (concurrent, limited by slowest)
val user = userDeferred.await()
val orders = ordersDeferred.await()
val balance = balanceDeferred.await()
user + mapOf("orders" to orders, "balance" to balance)
}
// Wrapping callback to suspend
suspend fun fetchDataFromCallback(): String = suspendCancellableCoroutine { cont ->
// Simulate callback-based API
val callback = object {
fun onSuccess(data: String) {
cont.resume(data) {}
}
fun onError(error: Exception) {
cont.resumeWithException(error)
}
}
// Simulate async callback
Thread {
Thread.sleep(100)
callback.onSuccess("callback-data")
}.start()
// Handle cancellation
cont.invokeOnCancellation {
println("Callback cancelled")
}
}
// Suspend function as parameter
suspend fun <T> retry(times: Int, block: suspend () -> T): T {
var lastException: Exception? = null
repeat(times) { attempt ->
try {
return block()
} catch (e: Exception) {
lastException = e
println("Attempt ${attempt + 1} failed: ${e.message}")
delay(100 * (attempt + 1).toLong())
}
}
throw lastException!!
}
// Suspend with timeout
suspend fun <T> fetchWithTimeout(timeoutMs: Long, block: suspend () -> T): T? =
withTimeoutOrNull(timeoutMs) { block() }
fun main() = runBlocking {
// Sequential
val seqTime = measureTimeMillis {
val profile = getUserProfile("user-1")
println("Sequential: $profile")
}
println("Sequential time: ${seqTime}ms\n")
// Concurrent
val concTime = measureTimeMillis {
val profile = getUserProfileFast("user-1")
println("Concurrent: $profile")
}
println("Concurrent time: ${concTime}ms\n")
// Callback wrapping
val callbackResult = fetchDataFromCallback()
println("Callback result: $callbackResult\n")
// Retry
var attempt = 0
val retried = retry(3) {
attempt++
if (attempt < 3) throw RuntimeException("Not ready")
"success"
}
println("Retried: $retried\n")
// Timeout
val fast = fetchWithTimeout(500) { fetchUser("1") }
println("Fast (should succeed): $fast")
val slow = fetchWithTimeout(50) {
delay(200)
"too slow"
}
println("Slow (should be null): $slow")
// Collection of suspend calls
println("\n--- Parallel fetch ---")
val ids = listOf("1", "2", "3", "4", "5")
val fetchTime = measureTimeMillis {
val users = ids.map { id -> async { fetchUser(id) } }.awaitAll()
println("Fetched ${users.size} users")
}
println("Parallel time: ${fetchTime}ms")
}Use Cases
- Sequential vs concurrent API calls
- Callback-to-coroutine conversion
- Retry and timeout patterns
Tags
Related Snippets
Similar patterns you can reuse in the same workflow.
kotlinbeginner
Null Safety — Elvis, Safe Call, and let
Master Kotlin null safety: safe calls, Elvis operator, let/also scoping, and smart casts.
Best for: Safe navigation through nullable chains
#kotlin#null-safety
kotlinbeginner
Data Classes — Copy, Destructure, and Equals
Use data classes for immutable models: auto-generated equals, hashCode, copy, and destructuring.
Best for: Immutable domain models and DTOs
#kotlin#data-class
kotlinbeginner
Scope Functions — let, run, apply, also, with
Master Kotlin scope functions: when to use let, run, apply, also, and with for concise code.
Best for: Object initialization and configuration
#kotlin#scope-functions
kotlinintermediate
Coroutines — launch, async, and Structured Concurrency
Write concurrent code with Kotlin coroutines: launch, async/await, structured concurrency, and dispatchers.
Best for: Parallel API calls in backend services
#kotlin#coroutines