跳至正文

🚀 為什麼要使用協程?

📖 什麼是協程?

**協程(Coroutines)**是 Kotlin 的核心功能,讓非同步程式設計變得簡單且直觀。擺脫複雜的回調地獄,就像撰寫同步程式碼一樣簡單!

💡 傳統非同步的問題

回調地獄

// ❌ 回調方式(難以閱讀和維護)
fun fetchUser(userId: String, callback: (User?) -> Unit) {
fetchFromNetwork(userId) { result ->
if (result != null) {
fetchProfile(result.id) { profile ->
if (profile != null) {
fetchPosts(profile.id) { posts ->
callback(User(result, profile, posts))
}
} else {
callback(null)
}
}
} else {
callback(null)
}
}
}

執行緒的成本

// ❌ 執行緒很昂貴
fun downloadFiles() {
repeat(1000) {
Thread {
// 每個執行緒都會消耗大量記憶體
downloadFile("file_$it.txt")
}.start()
}
// 可能會發生記憶體不足!
}

✨ 協程的優勢

1. 簡潔的程式碼

// ✅ 協程(易於閱讀且直觀)
suspend fun fetchUser(userId: String): User? {
val result = fetchFromNetwork(userId) ?: return null
val profile = fetchProfile(result.id) ?: return null
val posts = fetchPosts(profile.id)
return User(result, profile, posts)
}

2. 輕量級並行

// ✅ 即使數千個協程也沒問題
suspend fun downloadFiles() {
coroutineScope {
repeat(10000) {
launch {
// 輕量級協程!
downloadFile("file_$it.txt")
}
}
}
}

3. 結構化並行

// ✅ 自動管理生命週期
suspend fun processData() {
coroutineScope {
val job1 = launch { task1() }
val job2 = launch { task2() }

// 所有任務結束後自動完成
}
}

🎯 實際使用範例

網路請求

// 不需要回調,簡潔俐落!
suspend fun loadUserData(userId: String): UserData {
val user = apiService.getUser(userId)
val posts = apiService.getPosts(userId)
val friends = apiService.getFriends(userId)

return UserData(user, posts, friends)
}

UI 更新

// 不阻塞主執行緒
suspend fun updateUI() {
val data = withContext(Dispatchers.IO) {
// 在背景載入資料
loadDataFromDatabase()
}

// 在主執行緒更新 UI
updateViews(data)
}

並行處理

// 同時執行多個任務!
suspend fun fetchAllData(): CombinedData = coroutineScope {
val users = async { fetchUsers() }
val products = async { fetchProducts() }
val orders = async { fetchOrders() }

CombinedData(
users.await(),
products.await(),
orders.await()
)
}

🔥 協程 vs 執行緒

特徵協程執行緒
重量非常輕量笨重
建立成本幾乎沒有昂貴
數量可達數萬個數千個極限
切換成本
取消容易困難

效能比較

// 協程:執行 100,000 個
suspend fun testCoroutines() {
val time = measureTimeMillis {
coroutineScope {
repeat(100_000) {
launch {
delay(1000)
}
}
}
}
println("協程:${time}ms") // ~1000ms
}

// 執行緒:就算只有 10,000 個...
fun testThreads() {
val time = measureTimeMillis {
repeat(10_000) {
Thread {
Thread.sleep(1000)
}.start()
}
}
println("執行緒:${time}ms") // 可能記憶體不足!
}

🎨 協程的核心概念

Suspend(暫停)

// suspend 函式只能在協程中呼叫
suspend fun doWork() {
delay(1000) // 不阻塞執行緒的等待
println("任務完成")
}

結構化並行

// 以父子關係管理生命週期
fun main() = runBlocking {
launch { // 父層
launch { // 子層 1
delay(1000)
println("子層 1 完成")
}
launch { // 子層 2
delay(2000)
println("子層 2 完成")
}
println("父層等待子層結束")
}
}

🤔 常見問題

Q1. 協程是新的執行緒嗎?

**A:**不是!協程是在執行緒上執行的輕量級任務單位。

// 一個執行緒可以執行多個協程
fun main() = runBlocking {
repeat(3) { i ->
launch {
println("協程 $i${Thread.currentThread().name}")
}
}
}
// 可以全部在同一個執行緒中執行!

Q2. 需要將所有函式改為 suspend 嗎?

**A:**不需要!只有需要非同步作業的函式才需要改為 suspend。

// ❌ 不需要
suspend fun add(a: Int, b: Int) = a + b

// ✅ 需要
suspend fun fetchData() = withContext(Dispatchers.IO) {
loadFromNetwork()
}

Q3. 什麼時候應該使用協程?

**A:**在這些情況下使用!

// ✅ 網路請求
suspend fun loadData() = api.fetchData()

// ✅ 資料庫操作
suspend fun saveUser(user: User) = db.insert(user)

// ✅ 檔案 I/O
suspend fun readFile(path: String) = withContext(Dispatchers.IO) {
File(path).readText()
}

// ✅ 長時間計算任務
suspend fun heavyComputation() = withContext(Dispatchers.Default) {
// 複雜的計算
}

🎬 結語

用協程讓非同步程式設計變得簡單!

核心總結:
✅ 擺脫回調地獄
✅ 輕量級並行
✅ 直觀的程式碼
✅ 結構化的生命週期
✅ 比執行緒更有效率

下一步:在協程基礎中實際使用協程!