← Programming Core

KOTLIN CORE · 109

Coroutines:suspend 不等於「開一條 Thread」

Mobile app 要等 location、network、database,但不能因此把 UI thread 卡住。Coroutine 是 Kotlin 對非同步流程與 structured concurrency 的抽象;它和 thread 有關,但不是一個 coroutine 對應一條 OS thread。

Learning outcomes

1. 先拆四個名詞

概念先抓這個直覺
Coroutine可 suspend / resume 的工作單位
ThreadOS / runtime 執行 instruction 的 thread
Dispatcher決定 coroutine 工作在哪些 execution threads/context 上安排
Scope管理一組 coroutine 的 lifecycle / parent-child 關係

2. suspend function 不是自動 background

suspend fun loadTripPoints(): List<TripPoint> {
    return repository.fetchPoints()
}

suspend 只表示這個 function 可以呼叫其他 suspend function / 在 suspension point 暫停。它本身沒有保證「一定新開 thread」或「一定在 IO dispatcher」。

3. Suspension:等待時不必霸住同一條 thread

coroutine running→ suspend on I/O →thread 可做其他工作→ result ready →coroutine resumes

這和「blocking call 一直佔著 thread 等」是不同模型。

4. Dispatcher:工作性質與 execution context

withContext(Dispatchers.IO) {
    repository.readLargeFile()
}

概念上,blocking I/O 不應塞在 UI/main thread。CPU-heavy work、blocking I/O、UI update 的安排策略不同。

但不要硬背:實際 library 的 suspend API 可能已自行處理 threading。看到 suspend 不能直接推論「一定要包 Dispatchers.IO」。要看該 API contract。

5. Structured concurrency:child work 不應變成孤兒

suspend fun refreshTrip(): TripState =
    coroutineScope {
        val points = async {
            repository.fetchPoints()
        }

        val config = async {
            repository.fetchConfig()
        }

        buildState(
            points.await(),
            config.await()
        )
    }

coroutineScope 讓 child coroutines 受 parent scope 管理;scope 不完成,child work 就不會莫名其妙脫離整個操作。

6. Cancellation:使用者離開畫面,舊工作還需要嗎?

Coroutine cancellation 通常是 cooperative。Suspend functions 常會檢查 cancellation;CPU 密集 loop 則可能需要主動建立 cancellation point。

while (isActive) {
    updateComputation()
    yield()
}

這個概念到 Android lifecycle / ViewModel 時會非常重要。

7. Error propagation 也是 concurrency contract 的一部分

try {
    val state = refreshTrip()
    render(state)
} catch (e: IOException) {
    showNetworkError()
}

不要把 coroutine 當成「錯誤會自動消失」。parent-child 關係、exception propagation、supervision 都會影響 failure 行為。

8. Project checkpoint:Trip Meter Domain Core v3

class TripRepository(
    private val api: TripApi
) {
    suspend fun loadSnapshot(): TripState {
        val response = api.fetchTrip()

        return TripState(
            distanceKm = response.distanceKm,
            elapsedSeconds = response.elapsedSeconds,
            lastPoint = response.lastPoint
        )
    }
}

suspend fun refresh(
    repository: TripRepository
): Result<TripState> =
    try {
        Result.success(
            repository.loadSnapshot()
        )
    } catch (e: Exception) {
        Result.failure(e)
    }
async sourcesuspend repositorydomain TripStateResultAndroid UI later

到這裡 Kotlin core 仍然不依賴 Activity。下一課才把它接進 Android lifecycle / ViewModel state。

9. JavaScript await vs Kotlin suspend:相似,但不要視為同一機制

JavaScriptKotlin
常見 async abstractionPromise + async/awaitCoroutine + suspend
Thread modelBrowser/Node event-loop 模型為核心可跨 dispatcher / threads 安排
Cancellation通常需 API-specific mechanism,例如 AbortControllercoroutine job / structured cancellation 是核心模型

Final knowledge check

  1. 為什麼「suspend = background thread」是錯的?
  2. scope 解決的不是語法問題,而是什麼 lifecycle 問題?
  3. 如果 CPU loop 完全沒有 suspension point,cancellation 可能遇到什麼問題?
  4. 畫出 Trip Meter 從 async source → repository → domain state 的資料流。