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
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “Cú pháp”val job: Job = scope.launch { // Code chạy trong coroutine}Ví dụ
Phần tiêu đề “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?
Phần tiêu đề “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
Phần tiêu đề “Quản lý Job”val job = viewModelScope.launch { delay(5000) updateSomething()}
// Cancel job nếu cầnjob.cancel()
// Đợi job hoàn thànhjob.join()
// Kiểm tra trạng tháiif (job.isActive) { /* đang chạy */ }if (job.isCompleted) { /* đã xong */ }if (job.isCancelled) { /* đã bị cancel */ }2. async - Lấy kết quả
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “So sánh: Tuần tự vs Song song”// Tuần tự: 3 giâysuspend 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âysuspend 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
Phần tiêu đề “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
Phần tiêu đề “3. runBlocking - Bridge for Blocking Code”runBlocking block thread hiện tại cho đến khi coroutine hoàn thành.
Cú pháp
Phần tiêu đề “Cú pháp”fun main() = runBlocking { // Code chạy trong coroutine val data = fetchData() println(data)}Khi nào dùng?
Phần tiêu đề “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
Phần tiêu đề “Ví dụ trong tests”@Testfun testFetchData() = runBlocking { val repository = UserRepository() val user = repository.getUser(1) assertEquals("John", user.name)}4. coroutineScope - Tạo scope con
Phần tiêu đề “4. coroutineScope - Tạo scope con”coroutineScope tạo scope mới, đợi tất cả children hoàn thành.
Cú pháp
Phần tiêu đề “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
Phần tiêu đề “Đặ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ụ
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “So sánh”coroutineScope: Child1 fails → Child2 cancelled → Parent fails
supervisorScope: Child1 fails → Child2 continues → Parent continuesVí dụ tổng hợp
Phần tiêu đề “Ví dụ tổng hợp”1. Load và hiển thị dữ liệu
Phần tiêu đề “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
Phần tiêu đề “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
Phần tiêu đề “3. Timeout”suspend fun fetchWithTimeout(): Data? { return withTimeoutOrNull(5000) { // 5 giây timeout fetchData() }}
// Sử dụngval data = fetchWithTimeout()if (data == null) { showError("Request timed out")}📝 Tóm tắt
Phần tiêu đề “📝 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
Phần tiêu đề “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
Phần tiêu đề “Tiếp theo”Học về Dispatchers - điều hướng coroutines chạy trên thread nào.