βΈοΈ suspend Functions
π What is suspend?β
suspend functions are special functions that can suspend a coroutine. They can wait without blocking threads, making them efficient!
π‘ Basic Conceptsβ
suspend Keywordβ
// suspend functions can call other suspend functions
suspend fun doSomething() {
delay(1000) // suspend function
println("μλ£!")
}
fun main() = runBlocking {
doSomething() // Call within a coroutine
}
Regular Functions vs suspend Functionsβ
// β Cannot call suspend functions from regular functions
fun normalFunction() {
// delay(1000) // Compile error!
}
// β
Possible in suspend functions
suspend fun suspendFunction() {
delay(1000) // OK!
}
π― Practical Examplesβ
Network Requestβ
data class User(val id: String, val name: String)
suspend fun fetchUser(userId: String): User {
delay(1000) // Simulate network delay
return User(userId, "νκΈΈλ")
}
fun main() = runBlocking {
println("μμ² μμ")
val user = fetchUser("user123")
println("λ°μ μ μ : ${user.name}")
}
Sequential Executionβ
suspend fun prepareFood(): String {
delay(1000)
return "μμ"
}
suspend fun setTable(): String {
delay(500)
return "ν
μ΄λΈ"
}
suspend fun prepareMeal() {
println("μ€λΉ μμ")
val food = prepareFood()
println("$food μ€λΉ μλ£")
val table = setTable()
println("$table μ€λΉ μλ£")
println("λͺ¨λ μ€λΉ λ!")
}
fun main() = runBlocking {
val time = measureTimeMillis {
prepareMeal()
}
println("μμ μκ°: ${time}ms") // ~1500ms
}
Parallel Executionβ
suspend fun prepareMealParallel() = coroutineScope {
println("μ€λΉ μμ")
val foodDeferred = async { prepareFood() }
val tableDeferred = async { setTable() }
val food = foodDeferred.await()
val table = tableDeferred.await()
println("$food, $table μ€λΉ μλ£")
}
fun main() = runBlocking {
val time = measureTimeMillis {
prepareMealParallel()
}
println("μμ μκ°: ${time}ms") // ~1000ms (λ³λ ¬!)
}
π§ withContextβ
Dispatcher Switchingβ
suspend fun loadData(): String {
return withContext(Dispatchers.IO) {
// Run on IO thread
delay(1000)
"λ°μ΄ν°"
}
}
fun main() = runBlocking {
println("λ©μΈ μ°λ λ: ${Thread.currentThread().name}")
val data = loadData()
println("λ€μ λ©μΈ: ${Thread.currentThread().name}")
println("λ°μ΄ν°: $data")
}
Multiple Contextsβ
suspend fun processData() {
// IO operation
val data = withContext(Dispatchers.IO) {
readFromFile()
}
// CPU-intensive operation
val processed = withContext(Dispatchers.Default) {
heavyComputation(data)
}
// Main thread (UI update, etc.)
withContext(Dispatchers.Main) {
updateUI(processed)
}
}
suspend fun readFromFile(): String {
delay(500)
return "νμΌ λ΄μ©"
}
suspend fun heavyComputation(data: String): String {
delay(1000)
return data.uppercase()
}
suspend fun updateUI(data: String) {
println("UI μ
λ°μ΄νΈ: $data")
}
π¨ Advanced Patternsβ
Conditional Waitingβ
suspend fun fetchDataIfNeeded(cache: String?): String {
return cache ?: fetchFromNetwork()
}
suspend fun fetchFromNetwork(): String {
delay(1000)
return "μ λ°μ΄ν°"
}
fun main() = runBlocking {
// With cache - immediate return
val cached = fetchDataIfNeeded("μΊμ λ°μ΄ν°")
println(cached)
// No cache - network request
val fresh = fetchDataIfNeeded(null)
println(fresh)
}
Chainingβ
suspend fun step1(): Int {
delay(500)
return 10
}
suspend fun step2(input: Int): Int {
delay(500)
return input * 2
}
suspend fun step3(input: Int): Int {
delay(500)
return input + 5
}
suspend fun processChain(): Int {
val a = step1()
val b = step2(a)
val c = step3(b)
return c
}
fun main() = runBlocking {
val result = processChain()
println("μ΅μ’
κ²°κ³Ό: $result") // 25 (10 * 2 + 5)
}
Error Handlingβ
suspend fun riskyOperation(): String {
delay(1000)
throw Exception("λ€νΈμν¬ μ€λ₯!")
}
suspend fun safeOperation(): String? {
return try {
riskyOperation()
} catch (e: Exception) {
println("μλ¬ λ°μ: ${e.message}")
null
}
}
fun main() = runBlocking {
val result = safeOperation()
println("κ²°κ³Ό: ${result ?: "μ€ν¨"}")
}
π₯ Practical Functionsβ
Timeout Handlingβ
suspend fun fetchWithTimeout(): String? {
return try {
withTimeout(2000) {
delay(3000) // Takes too long
"λ°μ΄ν°"
}
} catch (e: TimeoutCancellationException) {
println("μκ° μ΄κ³Ό!")
null
}
}
fun main() = runBlocking {
val result = fetchWithTimeout()
println("κ²°κ³Ό: ${result ?: "νμμμ"}")
}
Retry Logicβ
suspend fun <T> retry(
times: Int = 3,
initialDelay: Long = 100,
factor: Double = 2.0,
block: suspend () -> T
): T {
var currentDelay = initialDelay
repeat(times - 1) { attempt ->
try {
return block()
} catch (e: Exception) {
println("μλ ${attempt + 1} μ€ν¨: ${e.message}")
}
delay(currentDelay)
currentDelay = (currentDelay * factor).toLong()
}
return block() // Last attempt
}
var attemptCount = 0
suspend fun unstableAPI(): String {
attemptCount++
if (attemptCount < 3) {
throw Exception("μΌμμ μ€λ₯")
}
return "μ±κ³΅!"
}
fun main() = runBlocking {
val result = retry(times = 5) {
unstableAPI()
}
println(result)
}
Cache Patternβ
class DataRepository {
private var cache: String? = null
suspend fun getData(forceRefresh: Boolean = false): String {
if (!forceRefresh && cache != null) {
println("μΊμμμ λ°ν")
return cache!!
}
println("λ€νΈμν¬μμ κ°μ Έμ€λ μ€...")
delay(1000)
val data = "μ λ°μ΄ν°"
cache = data
return data
}
}
fun main() = runBlocking {
val repo = DataRepository()
// First call - network
println(repo.getData())
// Second call - cache
println(repo.getData())
// Force refresh
println(repo.getData(forceRefresh = true))
}
π‘οΈ Cancellable Functionsβ
Cancellation Checkβ
suspend fun longRunningTask() {
repeat(10) { i ->
if (!isActive) {
println("μ·¨μλ¨!")
return
}
println("μμ
$i")
delay(500)
}
}
fun main() = runBlocking {
val job = launch {
longRunningTask()
}
delay(2000)
println("μμ
μ·¨μ!")
job.cancelAndJoin()
}
ensureActiveβ
suspend fun heavyWork() {
repeat(10) { i ->
ensureActive() // Throws exception if cancelled
println("λ¬΄κ±°μ΄ μμ
$i")
Thread.sleep(500) // Simulate CPU work
}
}
fun main() = runBlocking {
val job = launch {
try {
heavyWork()
} catch (e: CancellationException) {
println("μμ
μ΄ μ·¨μλμμ΅λλ€")
}
}
delay(2000)
job.cancel()
}
π€ Frequently Asked Questionsβ
Q1. Do suspend functions create threads?β
A: No! suspend only suspends without blocking threads.
suspend fun example() {
println("μμ: ${Thread.currentThread().name}")
delay(1000)
println("λ: ${Thread.currentThread().name}")
}
// Can run on the same thread!
Q2. What if I make all functions suspend?β
A: Unnecessary! Use it only when there are asynchronous operations.
// β Unnecessary
suspend fun add(a: Int, b: Int) = a + b
// β
Necessary
suspend fun fetchData() = withContext(Dispatchers.IO) {
// Network request
}
Q3. Can suspend functions call regular functions?β
A: Of course!
fun normalFunction() = println("μΌλ° ν¨μ")
suspend fun suspendFunction() {
normalFunction() // OK!
delay(1000)
}
π¬ Conclusionβ
Write efficient asynchronous code with suspend functions!
Key Points:
β
Suspend with suspend keyword
β
Wait without blocking threads
β
Switch threads with withContext
β
Control sequential/parallel execution
β
Cancellable operations
Next Step: Learn about asynchronous data streams in Flow!