Skip to Content

Suspend Functions

Suspend function là nền tảng của Kotlin Coroutines. Hiểu rõ cách chúng hoạt động sẽ giúp bạn viết code async hiệu quả.

Suspend Function là gì?

Suspend function là hàm có thể tạm dừng (suspend)tiếp tục (resume) sau đó mà không block thread hiện tại.

suspend fun fetchData(): String { delay(1000) // Tạm dừng 1 giây return "Data loaded!" }

Sự khác biệt với hàm thường

// Hàm thường - BLOCK thread fun normalFunction() { Thread.sleep(1000) // Thread bị block trong 1 giây } // Suspend function - KHÔNG block thread suspend fun suspendFunction() { delay(1000) // Thread được giải phóng trong 1 giây }

Minh họa:

Normal Function: Thread A: [====BLOCKED 1s====][done] Suspend Function: Thread A: [suspend][free to do other work...][resume][done]

Cách khai báo Suspend Function

Cú pháp cơ bản

suspend fun functionName(): ReturnType { // Code có thể suspend }

Ví dụ

// Suspend function trả về data suspend fun getUser(id: Int): User { delay(500) // Giả lập network call return User(id, "John") } // Suspend function không trả về gì suspend fun saveUser(user: User) { delay(500) database.save(user) } // Suspend function với nhiều tham số suspend fun searchUsers(query: String, limit: Int = 10): List<User> { delay(300) return repository.search(query, limit) }

Suspension Points

Suspension point là điểm trong code nơi coroutine có thể tạm dừng.

Suspension points bao gồm:

  1. Gọi suspend function khác:
suspend fun loadProfile() { val user = getUser(123) // Suspension point #1 val posts = getPosts(user) // Suspension point #2 display(user, posts) }
  1. Các hàm có sẵn: delay(), withContext(), yield()

  2. Custom suspend functions:

suspend fun fetchFromApi(): Data { return withContext(Dispatchers.IO) { api.getData() // Network call } }

Gọi Suspend Function

Suspend function chỉ có thể được gọi từ:

  1. Một suspend function khác
  2. Một coroutine (trong launch, async, v.v.)
// ❌ SAI - Không thể gọi từ hàm thường fun normalFunction() { val data = fetchData() // Compile error! } // ✅ ĐÚNG - Gọi từ suspend function khác suspend fun anotherSuspendFunction() { val data = fetchData() // OK! } // ✅ ĐÚNG - Gọi từ coroutine fun startLoading() { viewModelScope.launch { val data = fetchData() // OK! } }

Cách hoạt động bên trong (CPS Transformation)

Kotlin compiler biến suspend function thành Continuation Passing Style (CPS).

Code bạn viết:

suspend fun loadData(): String { val a = fetchA() val b = fetchB() return "$a $b" }

Compiler biến thành (đơn giản hóa):

fun loadData(continuation: Continuation<String>): Any { when (continuation.label) { 0 -> { continuation.label = 1 return fetchA(continuation) // Suspend tại đây } 1 -> { val a = continuation.a continuation.label = 2 return fetchB(continuation) // Suspend tại đây } 2 -> { val b = continuation.b return "$a $b" // Resume và return } } }

Continuation lưu trữ:

  • Trạng thái hiện tại (label)
  • Các biến local
  • Điểm sẽ resume

Ví dụ thực tế

1. Gọi API

// Repository class UserRepository(private val api: ApiService) { suspend fun getUser(id: Int): User { return withContext(Dispatchers.IO) { api.getUser(id) } } suspend fun updateUser(user: User) { withContext(Dispatchers.IO) { api.updateUser(user) } } } // ViewModel class UserViewModel(private val repository: UserRepository) : ViewModel() { fun loadUser(id: Int) { viewModelScope.launch { try { val user = repository.getUser(id) // Suspend tại đây _userState.value = UserState.Success(user) } catch (e: Exception) { _userState.value = UserState.Error(e.message) } } } }

2. Chạy tuần tự

suspend fun loadProfile(userId: Int): Profile { // Chạy tuần tự: user -> posts -> comments val user = getUser(userId) // Đợi xong rồi mới val posts = getUserPosts(user.id) // Đợi xong rồi mới val comments = getComments(posts) // Tiếp tục return Profile(user, posts, comments) }

3. Chạy song song với async

suspend fun loadDashboard(): Dashboard { // Chạy song song - nhanh hơn! return coroutineScope { val userDeferred = async { getUser() } val postsDeferred = async { getPosts() } val notificationsDeferred = async { getNotifications() } Dashboard( user = userDeferred.await(), posts = postsDeferred.await(), notifications = notificationsDeferred.await() ) } }

4. Retry với suspend function

suspend fun <T> retry( times: Int, initialDelay: Long = 100, maxDelay: Long = 1000, block: suspend () -> T ): T { var currentDelay = initialDelay repeat(times - 1) { try { return block() } catch (e: Exception) { // Log error } delay(currentDelay) currentDelay = (currentDelay * 2).coerceAtMost(maxDelay) } return block() // Last attempt } // Sử dụng val data = retry(times = 3) { api.getData() }

withContext - Chuyển đổi Dispatcher

withContext cho phép chạy suspend function trên Dispatcher khác:

suspend fun saveFile(content: String) { // Chuyển sang IO thread để ghi file withContext(Dispatchers.IO) { File("data.txt").writeText(content) } // Tự động quay về thread trước đó } suspend fun processData(items: List<Item>): Result { // Chuyển sang Default để tính toán nặng return withContext(Dispatchers.Default) { items.map { heavyComputation(it) } } }

📝 Tóm tắt

Khái niệmMô tả
suspendKeyword đánh dấu hàm có thể tạm dừng
Suspension pointĐiểm coroutine có thể pause
ContinuationObject lưu trạng thái để resume
withContextChuyển sang Dispatcher khác

Quy tắc

  1. Suspend function chỉ gọi được từ coroutine hoặc suspend function khác
  2. Không block thread - chỉ suspend coroutine
  3. Dùng withContext(Dispatchers.IO) cho I/O operations
  4. Dùng withContext(Dispatchers.Default) cho CPU-intensive work

Tiếp theo

Học về Coroutine Builders - các cách khởi chạy coroutine.

Last updated on