Skip to Content
Kotlin⚡ Kotlin CoroutinesCoroutine Builders

Coroutine Builders

Coroutine builders là các hàm để khởi chạy coroutines. Hiểu rõ từng builder giúp bạn chọn đúng công cụ cho từng trường hợp.

Các Builders chính

BuilderReturnUse case
launchJobFire-and-forget, không cần kết quả
asyncDeferredCần kết quả sau này
runBlockingTBridge blocking ↔ suspend (tests, main)
coroutineScopeTTạo scope mới trong suspend function

1. launch - Fire and Forget

launch tạo coroutine mới và trả về Job (không phải kết quả).

Cú pháp

val job: Job = scope.launch { // Code chạy trong coroutine }

Ví dụ

fun loadData() { viewModelScope.launch { // Task 1 - chạy ngầm val users = repository.getUsers() _usersState.value = users } // Code ở đây chạy NGAY LẬP TỨC // Không đợi coroutine trên hoàn thành Log.d("TAG", "Launch started!") }

Khi nào dùng launch?

  • Cập nhật UI
  • Gọi API không cần giá trị trả về ngay
  • Side effects (logging, analytics)
  • Background tasks

Quản lý Job

val job = viewModelScope.launch { delay(5000) updateSomething() } // Cancel job nếu cần job.cancel() // Đợi job hoàn thành job.join() // Kiểm tra trạng thái if (job.isActive) { /* đang chạy */ } if (job.isCompleted) { /* đã xong */ } if (job.isCancelled) { /* đã bị cancel */ }

2. async - Lấy kết quả

async tạo coroutine và trả về Deferred - có thể lấy kết quả bằng await().

Cú pháp

val deferred: Deferred<String> = scope.async { fetchData() // Trả về String } val result: String = deferred.await() // Đợi và lấy kết quả

Ví dụ: Chạy song song

suspend fun loadDashboard(): Dashboard { return coroutineScope { // Ba requests chạy ĐỒNG THỜI val userDeferred = async { getUser() } val postsDeferred = async { getPosts() } val statsDeferred = async { getStats() } // Đợi tất cả hoàn thành Dashboard( user = userDeferred.await(), posts = postsDeferred.await(), stats = statsDeferred.await() ) } }

Thời gian chạy: max(getUser, getPosts, getStats) thay vì tổng!

So sánh: Tuần tự vs Song song

// Tuần tự: 3 giây suspend fun sequential() { val a = apiA() // 1 giây val b = apiB() // 1 giây val c = apiC() // 1 giây process(a, b, c) } // Song song: 1 giây suspend fun parallel() = coroutineScope { val a = async { apiA() } // Bắt đầu ngay val b = async { apiB() } // Bắt đầu ngay val c = async { apiC() } // Bắt đầu ngay process(a.await(), b.await(), c.await()) }

Lazy async

Chỉ bắt đầu khi gọi await() hoặc start():

val deferred = async(start = CoroutineStart.LAZY) { expensiveComputation() } // Coroutine chưa bắt đầu... if (needResult) { val result = deferred.await() // Bắt đầu tại đây }

3. runBlocking - Bridge for Blocking Code

runBlocking block thread hiện tại cho đến khi coroutine hoàn thành.

Cú pháp

fun main() = runBlocking { // Code chạy trong coroutine val data = fetchData() println(data) }

Khi nào dùng?

Phù hợp:

  • main() function
  • Unit tests
  • Bridge legacy blocking code

Không dùng:

  • Android UI thread → Sẽ đơ app!
  • Production code (trong app)

Ví dụ trong tests

@Test fun testFetchData() = runBlocking { val repository = UserRepository() val user = repository.getUser(1) assertEquals("John", user.name) }

4. coroutineScope - Tạo scope con

coroutineScope tạo scope mới, đợi tất cả children hoàn thành.

Cú pháp

suspend fun process() = coroutineScope { launch { task1() } launch { task2() } // Đợi cả task1 và task2 hoàn thành mới return }

Đặc điểm

  1. Đợi children: Không return cho đến khi tất cả children xong
  2. Exception propagation: Nếu child fail → parent fail
  3. Cancellation: Cancel parent → cancel tất cả children

Ví dụ

suspend fun fetchAllData(): AllData = coroutineScope { val users = async { fetchUsers() } val products = async { fetchProducts() } AllData( users = users.await(), products = products.await() ) // Return khi cả hai đều xong }

5. supervisorScope - Độc lập giữa children

Khác coroutineScope, child failure không ảnh hưởng siblings.

suspend fun fetchMultiple() = supervisorScope { val job1 = launch { fetchData1() // Nếu fail... } val job2 = launch { fetchData2() // ...job2 vẫn tiếp tục! } }

So sánh

coroutineScope: Child1 fails → Child2 cancelled → Parent fails supervisorScope: Child1 fails → Child2 continues → Parent continues

Ví dụ tổng hợp

1. Load và hiển thị dữ liệu

class HomeViewModel : ViewModel() { fun loadHome() { viewModelScope.launch { try { _state.value = HomeState.Loading // Chạy song song val (user, posts) = coroutineScope { val userDef = async { userRepository.getCurrentUser() } val postsDef = async { postRepository.getPosts() } Pair(userDef.await(), postsDef.await()) } _state.value = HomeState.Success(user, posts) } catch (e: Exception) { _state.value = HomeState.Error(e.message) } } } }

2. Multiple API calls với retry

fun refreshAll() { viewModelScope.launch { supervisorScope { // Mỗi refresh độc lập - một cái fail không ảnh hưởng cái khác launch { refreshUsers() } launch { refreshProducts() } launch { refreshOrders() } } } }

3. Timeout

suspend fun fetchWithTimeout(): Data? { return withTimeoutOrNull(5000) { // 5 giây timeout fetchData() } } // Sử dụng val data = fetchWithTimeout() if (data == null) { showError("Request timed out") }

📝 Tóm tắt

BuilderReturnBlock Thread?Use Case
launchJobFire-and-forget
asyncDeferredParallel work, cần result
runBlockingTTests, main function
coroutineScopeT❌ (suspend)Group children
supervisorScopeTIndependent children

Quy tắc chọn builder

  1. Không cần kết quảlaunch
  2. Cần kết quảasync + await
  3. Chạy song song → Multiple async trong coroutineScope
  4. Children độc lậpsupervisorScope
  5. Bridge blocking coderunBlocking (chỉ tests/main)

Tiếp theo

Học về Dispatchers - điều hướng coroutines chạy trên thread nào.

Last updated on