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
| 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 |
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
- Đợi children: Không return cho đến khi tất cả children xong
- Exception propagation: Nếu child fail → parent fail
- 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 continuesVí 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
| 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 |
Quy tắc chọn builder
- Không cần kết quả →
launch - Cần kết quả →
async+await - Chạy song song → Multiple
asynctrongcoroutineScope - Children độc lập →
supervisorScope - Bridge blocking code →
runBlocking(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