Bỏ qua để đến nội dung

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.

Builder Return Use case
launch Job Fire-and-forget, không cần kết quả
async Deferred Cần kết quả sau này
runBlocking T Bridge blocking ↔ suspend (tests, main)
coroutineScope T Tạo scope mới trong suspend function

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

val job: Job = scope.launch {
// Code chạy trong coroutine
}
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!")
}
  • Cập nhật UI
  • Gọi API không cần giá trị trả về ngay
  • Side effects (logging, analytics)
  • Background tasks
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 */ }

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

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

// 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())
}

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
}

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

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

Phù hợp:

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

Không dùng:

  • Android UI thread → Sẽ đơ app!
  • Production code (trong app)
@Test
fun testFetchData() = runBlocking {
val repository = UserRepository()
val user = repository.getUser(1)
assertEquals("John", user.name)
}

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

suspend fun process() = coroutineScope {
launch { task1() }
launch { task2() }
// Đợi cả task1 và task2 hoàn thành mới return
}
  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
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
}

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!
}
}
coroutineScope:
Child1 fails → Child2 cancelled → Parent fails
supervisorScope:
Child1 fails → Child2 continues → Parent continues

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)
}
}
}
}
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() }
}
}
}
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")
}

Builder Return Block Thread? Use Case
launch Job Fire-and-forget
async Deferred Parallel work, cần result
runBlocking T Tests, main function
coroutineScope T ❌ (suspend) Group children
supervisorScope T Independent children
  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)

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