Kotlin Coroutines Productivity
Kotlin Coroutines Team Productivity Async Structured Concurrency Flow StateFlow Testing suspend CoroutineScope Production
| Feature | Coroutines | RxJava | Callback |
|---|---|---|---|
| Readability | เหมือน Sync Code | Chain operators | Nested callbacks |
| Error Handling | try-catch ปกติ | onError operator | Error callback |
| Cancellation | Structured (อัตโนมัติ) | Disposable (manual) | Manual flag |
| Testing | runTest + TestDispatcher | TestScheduler | ยาก |
| Learning Curve | ต่ำ (คล้าย Sync) | สูง (operators มาก) | ต่ำ แต่ซับซ้อนเร็ว |
| Backpressure | Flow (built-in) | Flowable | ไม่มี |
Coroutines Fundamentals
# === Kotlin Coroutines ===
# // Basic coroutine builders
# import kotlinx.coroutines.*
#
# fun main() = runBlocking {
# // launch: Fire-and-forget
# val job = launch {
# delay(1000)
# println("World!")
# }
# println("Hello,")
# job.join()
#
# // async: Return value
# val deferred = async {
# fetchUserFromAPI()
# }
# val user = deferred.await()
#
# // Parallel execution
# val (user, orders) = coroutineScope {
# val userDeferred = async { fetchUser(userId) }
# val ordersDeferred = async { fetchOrders(userId) }
# Pair(userDeferred.await(), ordersDeferred.await())
# }
#
# // withContext: Switch dispatcher
# val result = withContext(Dispatchers.IO) {
# database.query("SELECT * FROM users")
# }
# }
#
# // suspend function
# suspend fun fetchUser(id: String): User {
# return withContext(Dispatchers.IO) {
# apiClient.get("/users/$id")
# }
# }
#
# // Structured Concurrency
# suspend fun processOrder(orderId: String) = coroutineScope {
# val payment = async { processPayment(orderId) }
# val inventory = async { updateInventory(orderId) }
# val notification = async { sendNotification(orderId) }
# // If any fails, all are cancelled automatically
# Triple(payment.await(), inventory.await(), notification.await())
# }
from dataclasses import dataclass
@dataclass
class CoroutineBuilder:
builder: str
returns: str
use_case: str
cancellation: str
example: str
builders = [
CoroutineBuilder("launch",
"Job (ไม่มี Return Value)",
"Fire-and-forget เช่น Log Analytics UI Update",
"Cancel ผ่าน job.cancel()",
"launch { sendAnalytics(event) }"),
CoroutineBuilder("async",
"Deferred (Return Value)",
"Parallel execution รอผลลัพธ์",
"Cancel ผ่าน deferred.cancel()",
"val user = async { fetchUser() }.await()"),
CoroutineBuilder("withContext",
"T (Return Value, switch context)",
"สลับ Dispatcher เช่น IO → Main",
"Cancel ตาม Parent Scope",
"withContext(IO) { db.query() }"),
CoroutineBuilder("coroutineScope",
"T (Structured scope)",
"กลุ่ม Coroutines ที่ต้อง Cancel ด้วยกัน",
"Cancel ทั้งหมดเมื่อ 1 ตัว Fail",
"coroutineScope { async{} + async{} }"),
CoroutineBuilder("runBlocking",
"T (Block current thread)",
"Main function, Test only อย่าใช้ใน Production",
"Block Thread จริงๆ",
"fun main() = runBlocking { ... }"),
]
print("=== Coroutine Builders ===")
for b in builders:
print(f" [{b.builder}] Returns: {b.returns}")
print(f" Use: {b.use_case}")
print(f" Cancel: {b.cancellation}")
print(f" Example: {b.example}")
Flow Patterns
# === Kotlin Flow ===
# // StateFlow (State Management)
# class UserViewModel : ViewModel() {
# private val _uiState = MutableStateFlow(UiState.Loading)
# val uiState: StateFlow = _uiState.asStateFlow()
#
# fun loadUser(id: String) {
# viewModelScope.launch {
# _uiState.value = UiState.Loading
# try {
# val user = repository.fetchUser(id)
# _uiState.value = UiState.Success(user)
# } catch (e: Exception) {
# _uiState.value = UiState.Error(e.message)
# }
# }
# }
# }
#
# // Flow operators
# repository.getUsers()
# .map { users -> users.filter { it.isActive } }
# .distinctUntilChanged()
# .debounce(300)
# .flowOn(Dispatchers.Default)
# .catch { e -> emit(emptyList()) }
# .onCompletion { println("Done") }
# .collect { activeUsers -> updateUI(activeUsers) }
#
# // SharedFlow (Events)
# class EventBus {
# private val _events = MutableSharedFlow()
# val events: SharedFlow = _events.asSharedFlow()
# suspend fun emit(event: Event) = _events.emit(event)
# }
@dataclass
class FlowType:
type_: str
behavior: str
replay: str
use_case: str
alternative: str
flows = [
FlowType("Flow (Cold)",
"สร้างใหม่ทุกครั้งที่ collect ไม่เก็บค่า",
"ไม่มี", "API Call, Database Query, File Read",
"RxJava Observable"),
FlowType("StateFlow (Hot)",
"เก็บค่าล่าสุดเสมอ Emit ค่าใหม่เมื่อเปลี่ยน",
"1 (ค่าล่าสุด)", "UI State, Configuration, Settings",
"LiveData, BehaviorSubject"),
FlowType("SharedFlow (Hot)",
"Broadcast Event ให้หลาย Collector",
"กำหนดเอง (0, 1, N)", "Event Bus, Navigation, Toast/Snackbar",
"PublishSubject, EventBus"),
]
print("=== Flow Types ===")
for f in flows:
print(f" [{f.type_}] {f.behavior}")
print(f" Replay: {f.replay}")
print(f" Use: {f.use_case}")
print(f" Alt: {f.alternative}")
Testing
# === Coroutine Testing ===
# // Test suspend function
# @Test
# fun `fetchUser returns correct user`() = runTest {
# val repo = UserRepository(FakeApiClient())
# val user = repo.fetchUser("123")
# assertEquals("John", user.name)
# }
#
# // Test Flow with Turbine
# @Test
# fun `uiState emits loading then success`() = runTest {
# val viewModel = UserViewModel(FakeRepository())
# viewModel.uiState.test {
# assertEquals(UiState.Loading, awaitItem())
# viewModel.loadUser("123")
# assertEquals(UiState.Loading, awaitItem())
# assertEquals(UiState.Success(testUser), awaitItem())
# cancelAndIgnoreRemainingEvents()
# }
# }
#
# // Test with delay
# @Test
# fun `debounced search works`() = runTest {
# val viewModel = SearchViewModel()
# viewModel.search("kot")
# advanceTimeBy(200) // ยังไม่ถึง debounce 300ms
# assertEquals(emptyList(), viewModel.results.value)
# advanceTimeBy(200) // ผ่าน 300ms แล้ว
# assertEquals(listOf("kotlin"), viewModel.results.value)
# }
@dataclass
class TestTool:
tool: str
purpose: str
api: str
example: str
tools = [
TestTool("runTest",
"รัน Test ที่มี suspend function",
"runTest { ... }",
"runTest { val result = repo.fetch(); assert(result) }"),
TestTool("TestDispatcher",
"Control Dispatcher ใน Test ควบคุมเวลา",
"StandardTestDispatcher / UnconfinedTestDispatcher",
"Dispatchers.setMain(testDispatcher)"),
TestTool("advanceTimeBy",
"ข้ามเวลาใน Test ไม่ต้องรอ delay จริง",
"advanceTimeBy(1000)",
"viewModel.search(); advanceTimeBy(300)"),
TestTool("Turbine",
"Test Flow ง่ายมาก await แต่ละ emission",
"flow.test { awaitItem(); awaitComplete() }",
"stateFlow.test { assertEquals(Loading, awaitItem()) }"),
TestTool("Mockk coEvery",
"Mock suspend function",
"coEvery { repo.fetchUser(any()) } returns testUser",
"coVerify { repo.fetchUser(\"123\") }"),
]
print("=== Testing Tools ===")
for t in tools:
print(f" [{t.tool}] {t.purpose}")
print(f" API: {t.api}")
print(f" Example: {t.example}")
เคล็ดลับ
- Structured: ใช้ coroutineScope ไม่ใช่ GlobalScope ป้องกัน Leak
- StateFlow: ใช้ StateFlow แทน LiveData ใน Modern Android
- Dispatcher: ใช้ withContext(IO) สำหรับ IO อย่ารันบน Main
- Test: ใช้ runTest + Turbine ทดสอบ Coroutine และ Flow
- Cancel: ตรวจ isActive ใน Loop ยาว ให้ Cancel ได้ถูกต้อง
Kotlin Coroutines คืออะไร
Async Code suspend function CoroutineScope launch async withContext Flow Structured Concurrency Cancel Android Ktor Spring Boot
ช่วยเพิ่ม Productivity อย่างไร
อ่านง่าย ลด Bug Race Condition Test ง่าย runTest ลดเวลาเขียน 30-50% Error try-catch Cancel อัตโนมัติ เรียนรู้เร็ว IDE Debug
Flow คืออะไร
Cold Async Stream emit collect map filter StateFlow State Management SharedFlow Event Broadcasting flowOn catch onCompletion Operator
Testing ทำอย่างไร
runTest TestDispatcher advanceTimeBy Turbine Flow awaitItem awaitComplete Mockk coEvery coVerify suspend function UnconfinedTestDispatcher
สรุป
Kotlin Coroutines Team Productivity suspend launch async Flow StateFlow Structured Concurrency Testing runTest Turbine Production
