Coroutines ใน Kotlin: ทำให้การเขียนโปรแกรมแบบอะซิงโครนัสง่ายขึ้น

เริ่มโด Ovtak, ธ.ค 26, 2025, 03:27 หลังเ่ยง

หัวอก่อนหน้า - หัวข้อถัดไ

Ovtak

Kotlin นำเสนอโครงสร้างที่ทรงพลังและเบาสำหรับการเขียนโปรแกรมแบบอะซิงโครนัสที่เรียกว่า Coroutines แทนที่จะใช้ callback แบบดั้งเดิม RxJava หรือ Thread คุณสามารถเขียนโค้ดของคุณให้ดูเหมือนซิงโครนัส แต่ทำงานแบบอะซิงโครนัสในเบื้องหลัง ด้วยวิธีนี้ คุณสามารถจัดการการเรียกเครือข่าย การดำเนินการฐานข้อมูล งานที่ใช้เวลานาน และการดำเนินการที่บล็อกได้อย่างง่ายดาย ในบทความนี้ เราจะตรวจสอบแนวคิดพื้นฐานของ coroutines รวมถึง launch, async/await, scope และตัวอย่างการใช้งานทั่วไปอย่างละเอียด

พื้นฐานของ Coroutines
เพื่อใช้ coroutines ก่อนอื่นคุณต้องเพิ่ม dependency (Gradle):
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3"

โครงสร้างพื้นฐาน:
  • Coroutine Scope: จัดการวงจรชีวิตของ coroutines (GlobalScope, lifecycleScope, viewModelScope เป็นต้น)
  • Coroutine Builder: launch (fire-and-forget), async (คืนค่า)
  • Dispatcher: ที่ที่ทำงาน (Main, IO, Default)

ตัวอย่างแรก: launch
import kotlinx.coroutines.*

fun main() {
    GlobalScope.launch(Dispatchers.Main) {
        println("Coroutine เริ่มต้น - Thread: ${Thread.currentThread().name}")
        delay(1000) // การรอแบบไม่บล็อก (ไม่ใช่ sleep!)
        println("1 วินาทีต่อมา")
    }
    println("เธรดหลักดำเนินต่อ")
    Thread.sleep(2000) // บล็อกเธรดหลัก (สำหรับทดสอบ)
}

delay() ไม่บล็อกเธรด แต่เพียงแค่ระงับ coroutine

async และ await
สำหรับการดำเนินการอะซิงโครนัสที่คืนค่า:
GlobalScope.launch(Dispatchers.Main) {
    val sonuc1 = async(Dispatchers.IO) { ağÇağrısı1() }
    val sonuc2 = async(Dispatchers.IO) { ağÇağrısı2() }
    println("รวม: ${sonuc1.await() + sonuc2.await()}")
}

fun ağÇağrısı1(): Int {
    Thread.sleep(1000)
    return 42
}

fun ağÇağrısı2(): Int {
    Thread.sleep(1000)
    return 58
}

การเรียกทั้งสองทำงานแบบขนาน เวลารวมประมาณ 1 วินาที

การใช้งาน Scope และ Context
  • GlobalScope: ทั่วทั้งแอปพลิเคชัน ใช้อย่างระมัดระวัง (เสี่ยง memory leak)
  • lifecycleScope (Android): ผูกกับวงจรชีวิตของ Activity/Fragment
  • viewModelScope (ViewModel): ยกเลิกอัตโนมัติกับ ViewModel

ตัวอย่าง Android:
class MyViewModel : ViewModel() {
    fun veriYukle() {
        viewModelScope.launch {
            try {
                val veri = withContext(Dispatchers.IO) {
                    repository.veriGetir() // อัปเดต UI
                }
            } catch (e: Exception) {
                // จัดการข้อผิดพลาด
            }
        }
    }
}

Structured Concurrency และ Exception Handling

viewModelScope.launch {
    try {
        val job = launch {
            throw Exception("ข้อผิดพลาด!")
        }
        job.join()
    } catch (e: Exception) {
        println("จับได้: ${e.message}")
    }
}

// ด้วย SupervisorJob ข้อผิดพลาดใน coroutine ลูกไม่กระทบ parent
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)

Dispatcher ทั่วไป
Dispatcher.Main: การอัปเดต UI (Android)
Dispatcher. IO: เครือข่าย ไฟล์ ฐานข้อมูล
Dispatcher.Default: การดำเนินการที่ใช้ CPU สูง (เช่น sorting เป็นต้น)
Dispatcher.Unconfined: สถานการณ์พิเศษ (สำหรับทดสอบ)

แนวปฏิบัติที่ดีที่สุด
  • ใช้ structured concurrency เสมอ (แทน GlobalScope ใช้ builder ที่มี scope)
  • ใช้ Dispatchers.Main ในเธรด UI
  • ใช้ withContext() เพื่อเปลี่ยนเธรด
  • เพิ่ม try-catch และ CoroutineExceptionHandler สำหรับการจัดการข้อผิดพลาด
  • จัดการการไหลข้อมูลแบบ reactive ด้วย Flow (ระดับสูง)

สรุป
Kotlin coroutines ทำให้การเขียนโปรแกรมแบบอะซิงโครนัสอ่านง่ายและปลอดภัยมากขึ้น
หลุดพ้นจาก callback hell คุณสามารถเขียนโค้ดของคุณให้ดูเหมือนซิงโครนัส เริ่มต้นด้วย launch และ async อย่างง่าย ในโปรเจกต์ Android ดำเนินต่อด้วย lifecycleScope และ viewModelScope
เพื่อฝึกฝน ลองเขียนแอปพลิเคชัน Android ง่ายๆ ที่เรียกเครือข่าย – เช่น แอปพยากรณ์อากาศที่ดึงข้อมูล JSON!