suspend function: คืออะไร ไวยากรณ์ และการทำงานในโครูทีน

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-06-22 เวลาอ่าน: 9 นาที

Suspend function คือฟังก์ชันที่มีตัวปรับ suspend ซึ่งสามารถหยุดการทำงานชั่วคราวโดยไม่บล็อกเธรดและกลับมาเริ่มใหม่ได้ในโครูทีนเดียวกัน ตาม JetBrains Kotlin Docs, 2025 ฟังก์ชัน suspend เป็นส่วนประกอบพื้นฐานของโครูทีนที่ให้ความไม่พร้อมกันโดยไม่ต้องใช้ callback ฟังก์ชัน suspend แต่ละฟังก์ชันถูกคอมไพล์เป็นเครื่องสถานะบนพื้นฐานของ Continuation ซึ่งช่วยให้จัดการจุดหยุดชั่วคราวได้อย่างมีประสิทธิภาพ

ประเด็นสำคัญ

  • Suspend — คำสำคัญใน Kotlin ที่ระบุว่าฟังก์ชันสามารถหยุดชั่วคราวได้ (ไม่พร้อมกัน)
  • Continuation — พารามิเตอร์ที่ซ่อนอยู่ซึ่งคอมไพเลอร์เพิ่มในทุกฟังก์ชัน suspend เพื่อบันทึกสถานะ
  • จุดหยุดชั่วคราว — ตำแหน่งที่เรียกฟังก์ชัน suspend อื่น ๆ ซึ่งโครูทีนสามารถหยุดได้โดยไม่บล็อก
  • เครื่องสถานะ — การแสดงภายในของฟังก์ชัน suspend ที่แต่ละจุดหยุดชั่วคราวเป็นสถานะแยกต่างหาก
  • เรียกจากโครูทีนเท่านั้น — ฟังก์ชัน suspend สามารถเรียกได้จากฟังก์ชัน suspend อื่นหรือจาก launch/async เท่านั้น

suspend function ใน Kotlin คืออะไร?

Suspend function คือฟังก์ชันที่ประกาศด้วยคำสำคัญ suspend ซึ่งสามารถหยุดการทำงานชั่วคราวที่จุดหนึ่งจุดหรือมากกว่าโดยไม่บล็อกเธรด ทุกครั้งที่เรียกฟังก์ชัน suspend ภายในฟังก์ชัน suspend อื่นคือจุดหยุดชั่วคราวที่อาจเกิดขึ้น

kotlin
suspend fun fetchUserData(): User {
    val response = httpClient.get("/user")
    return parser.parse(response)
}

คอมไพเลอร์ Kotlin แปลงฟังก์ชันดังกล่าวเป็นเครื่องสถานะ จุดหยุดชั่วคราวแต่ละจุด (การเรียกฟังก์ชัน suspend อื่น) กลายเป็นสถานะ (label) เธรดปัจจุบันถูกปล่อยระหว่างสถานะ และหลังจากการดำเนินการที่รอคอยเสร็จสมบูรณ์ การทำงานจะกลับมาทำงานต่อจากสถานะถัดไป

ประวัติ

ฟังก์ชัน suspend ปรากฏใน Kotlin 1.3 (2018) พร้อมกับโครูทีนในฐานะคุณสมบัติทดลองและกลายเป็นเสถียรใน Kotlin 1.5 (2021) ก่อนหน้านั้น ความไม่พร้อมกันใน Kotlin/Java ทำได้ผ่าน callback, RxJava และ CompletableFuture ฟังก์ชัน suspend เสนอทางเลือกที่มีไวยากรณ์เชิงเส้นและการจัดการเธรดอัตโนมัติ

ฟังก์ชัน suspend ทำงานอย่างไร: Continuation และเครื่องสถานะ

การเข้าใจการทำงานภายในของฟังก์ชัน suspend คือกุญแจสำคัญในการทำงานกับโครูทีนอย่างถูกต้อง ซึ่งแตกต่างจากฟังก์ชันปกติ ฟังก์ชัน suspend แต่ละฟังก์ชันถูกคอมไพล์เป็นคลาสที่มีอินเทอร์เฟซ Continuation

Continuation — พารามิเตอร์ที่ซ่อนอยู่

คอมไพเลอร์ Kotlin เพิ่มพารามิเตอร์ชนิด Continuation ที่ท้ายรายการพารามิเตอร์ของฟังก์ชัน suspend แต่ละฟังก์ชัน Continuation ประกอบด้วย:

  • context — CoroutineContext (ตัวจัดส่ง, job, องค์ประกอบบริบท)
  • resumeWith — เมธอดสำหรับกลับมาทำงานต่อด้วยผลลัพธ์หรือข้อยกเว้น
  • label — ดัชนีของสถานะปัจจุบันในเครื่องสถานะ

ตัวอย่างเครื่องสถานะ

สมมติว่าเรามีฟังก์ชัน suspend ที่มีการเรียกฟังก์ชัน suspend อื่นสองครั้ง:

kotlin
suspend fun process() {
    val a = stepOne()
    val b = stepTwo(a)
    println(b)
}

คอมไพเลอร์แปลงเป็นเครื่องสถานะที่มีป้ายกำกับ:

kotlin
// Simplified generated code representation
fun process(cont: Continuation<Unit>): Any? {
    val cont = cont as ProcessContinuation
    when (cont.label) {
        0 -> {
            cont.label = 1
            if (stepOne(cont) == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED
        }
        1 -> {
            cont.label = 2
            val a = cont.result as TypeA
            if (stepTwo(a, cont) == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED
        }
        2 -> {
            println(cont.result)
            Unit
        }
    }
}

ข้อสังเกตสำคัญ: ถ้าฟังก์ชันคืนค่า COROUTINE_SUSPENDED เธรดปัจจุบันจะถูกปล่อย เมื่อการดำเนินการแบบไม่พร้อมกันเสร็จสมบูรณ์ Continuation.resumeWith จะถูกเรียกและเครื่องสถานะดำเนินการต่อจากป้ายกำกับถัดไป

ไวยากรณ์ของฟังก์ชัน suspend: การประกาศและการเรียกใช้

การประกาศฟังก์ชัน suspend ไม่แตกต่างจากฟังก์ชันปกติ ยกเว้นคำสำคัญ suspend ก่อน fun มีข้อจำกัดเพียงข้อเดียว: ฟังก์ชัน suspend สามารถเรียกได้จากโครูทีนหรือฟังก์ชัน suspend อื่นเท่านั้น

การประกาศพื้นฐาน

kotlin
suspend fun delayAndReturn(ms: Long): String {
    delay(ms)
    return "Done after ${ms}ms"
}

ในตัวอย่างนี้ delay ก็เป็นฟังก์ชัน suspend ที่หยุดโครูทีนตามจำนวนมิลลิวินาทีที่ระบุโดยไม่บล็อกเธรด หลังจากหน่วงเวลา การทำงานจะกลับมาดำเนินการต่อ

การเรียกจากโครูทีน

kotlin
fun main() = runBlocking {
    val result = delayAndReturn(1000)
    println(result)
}

runBlocking สร้างสะพานเชื่อมระหว่างโลกปกติและโครูทีน ภายใน lambda สามารถเรียกฟังก์ชัน suspend ใด ๆ ได้

Suspend lambda และชนิดฟังก์ชัน

Kotlin รองรับเวอร์ชัน suspend ของชนิดฟังก์ชัน — suspend () -> T และ suspend (A) -> B ซึ่งอนุญาตให้ส่ง lambda แบบไม่พร้อมกันไปยังฟังก์ชันลำดับสูง:

kotlin
suspend fun  withRetry(
    retries: Int = 3,
    block: suspend () -> T
): T {
    repeat(retries - 1) {
        try { return block() }
        catch (_: Exception) { delay(100) }
    }
    return block()
}

ฟังก์ชัน withRetry รับ lambda แบบ suspend และลองดำเนินการใหม่เมื่อเกิดข้อผิดพลาด นี่เป็นรูปแบบทั่วไปสำหรับคำขอเครือข่ายที่มีการลองใหม่

ความแตกต่างระหว่างฟังก์ชัน suspend และฟังก์ชันปกติ

ความแตกต่างระหว่างฟังก์ชัน suspend และฟังก์ชันปกติไม่ได้มีเพียงแค่การเพิ่มตัวปรับ มาดูความแตกต่างหลักกัน

ลักษณะฟังก์ชันปกติฟังก์ชัน suspend
เธรดการทำงานบล็อกเธรดจนกว่าจะเสร็จสามารถปล่อยเธรดและกลับมาทำงานต่อได้
พารามิเตอร์ของคอมไพเลอร์เฉพาะพารามิเตอร์ที่ระบุContinuation โดยนัยที่ท้าย
เรียกจากฟังก์ชันปกติได้ไม่ได้
สแต็กสแต็กทางกายภาพของเธรดเครื่องสถานะในฮีป + สแต็กทางกายภาพระหว่างจุด
ค่าที่คืนค่าโดยตรงค่าหรือ COROUTINE_SUSPENDED
ประสิทธิภาพค่าใช้จ่ายน้อยที่สุด~ไม่กี่นาโนวินาทีต่อเครื่องสถานะ (Kotlin 1.9+)

เหตุใดจึงไม่สามารถเรียกฟังก์ชัน suspend จากฟังก์ชันปกติ

ฟังก์ชันปกติไม่มี Continuation — ไม่มีที่สำหรับบันทึกสถานะและไม่มีอะไรสำหรับกลับมาทำงานต่อ หากคุณต้องการเรียกฟังก์ชัน suspend จากฟังก์ชันปกติ ให้ใช้ runBlocking (สำหรับการทดสอบ) หรือ CoroutineScope.launch (สำหรับการผลิตโดยคำนึงถึงวงจรชีวิต)

ตัวอย่างฟังก์ชัน suspend ใน Android

มาดูสามสถานการณ์จริงของการใช้ฟังก์ชัน suspend ในแอป Android ด้วย Kotlin กัน

ตัวอย่างที่ 1: Room DAO พร้อมคำสั่ง suspend

Room รองรับฟังก์ชัน suspend โดยตรง — คำสั่งถูกดำเนินการบนเธรดพื้นหลังโดยอัตโนมัติ:

kotlin
@Dao
interface UserDao {
    @Query("SELECT * FROM users WHERE id = :id")
    suspend fun getUser(id: Int): User?

    @Insert
    suspend fun insertUser(user: User)
}

Room ใช้ Dispatchers.IO ภายในเพื่อดำเนินการคำสั่ง และผลลัพธ์จะถูกส่งคืนบนตัวจัดส่งที่เรียกฟังก์ชัน suspend

ตัวอย่างที่ 2: การประกอบฟังก์ชัน suspend สำหรับโหลดหน้าจอ

kotlin
class ProfileViewModel : ViewModel() {
    private val repo = ProfileRepository()

    fun loadProfile(id: String) {
        viewModelScope.launch {
            val profile = repo.getProfile(id)
            _profile.value = profile
        }
    }
}

ViewModelScope.launch สร้างโครูทีน ซึ่งภายในเรียกฟังก์ชัน suspend getProfile หลังจากได้รับผลลัพธ์ ส่วนติดต่อผู้ใช้จะถูกอัปเดตบนเธรดหลัก

ตัวอย่างที่ 3: ขั้นตอนไม่พร้อมกันตามลำดับ

kotlin
suspend fun placeOrder(cart: Cart): OrderResult {
    val validated = validateCart(cart)
    val payment = processPayment(validated)
    val receipt = sendReceipt(payment)
    return receipt
}

ฟังก์ชัน suspend สามฟังก์ชันทำงานตามลำดับ ในแต่ละขั้นตอน โครูทีนสามารถหยุดชั่วคราวได้โดยไม่บล็อกเธรด ถ้าขั้นตอนใดเกิดข้อยกเว้น ขั้นตอนที่เหลือจะไม่ทำงาน ป้องกันสถานะคำสั่งซื้อที่ไม่ถูกต้อง

ข้อผิดพลาดทั่วไปเมื่อทำงานกับฟังก์ชัน suspend

แม้แต่นักพัฒนา Kotlin ที่มีประสบการณ์ก็ยังทำผิดพลาดเมื่อออกแบบฟังก์ชัน suspend มาดูข้อผิดพลาดที่พบบ่อยที่สุดกัน

ข้อผิดพลาด 1: การเรียกแบบบล็อกภายใน suspend

ฟังก์ชัน suspend ไม่ได้ทำให้โค้ดไม่พร้อมกันโดยอัตโนมัติ Thread.sleep(), InputStream.read() และการเรียกแบบบล็อกอื่น ๆ ยังคงบล็อกเธรด ใช้ withContext(Dispatchers.IO) เพื่อครอบคลุมการดำเนินการแบบบล็อก

ข้อผิดพลาด 2: การสร้างฟังก์ชัน suspend โดยไม่จำเป็น

ถ้าฟังก์ชันไม่เรียกฟังก์ชัน suspend อื่นและไม่ดำเนินการแบบไม่พร้อมกัน — ตัวปรับ suspend นั้นซ้ำซ้อน มันเพิ่มค่าใช้จ่ายให้กับเครื่องสถานะและจำกัดบริบทการเรียก ทำให้ฟังก์ชันเป็น suspend เฉพาะเมื่อมันหยุดชั่วคราวจริง ๆ

ข้อผิดพลาด 3: การละเลย CancellationException

เมื่อโครูทีนถูกยกเลิก ฟังก์ชัน suspend จะโยน CancellationException อย่าจับมันโดยไม่คิด — คุณกำลังทำให้โค้ดที่เรียกสูญเสียความสามารถในการดำเนินการยกเลิกอย่างถูกต้อง ถ้าคุณต้องการดำเนินการสิ้นสุด ให้ใช้บล็อก finally และ NonCancellable

kotlin
suspend fun safeOperation() {
    try {
        doWork()
    } finally {
        withContext(NonCancellable) {
            cleanup()
        }
    }
}

บล็อก finally จะทำงานเสมอ รวมถึงเมื่อยกเลิก และ NonCancellable รับประกันว่าการทำความสะอาดจะไม่ถูกขัดจังหวะ

ข้อผิดพลาด 4: การเรียกฟังก์ชัน suspend จาก callback

คุณไม่สามารถเรียกฟังก์ชัน suspend โดยตรงจาก callback โดยไม่สร้างโครูทีน ใช้ suspendCoroutine หรือ suspendCancellableCoroutine เพื่อปรับสไตล์ callback ให้เข้ากับโครูทีน

คำถามที่พบบ่อย

ฟังก์ชัน suspend ไม่มีจุดหยุดชั่วคราวได้หรือไม่?

ได้ ในทางเทคนิคฟังก์ชัน suspend สามารถไม่เรียกฟังก์ชัน suspend อื่นได้ คอมไพเลอร์จะสร้างเครื่องสถานะที่มีสถานะเดียว (label 0) อย่างไรก็ตาม ไม่มีประโยชน์ในทางปฏิบัติสำหรับฟังก์ชันดังกล่าว — มันทำงานเหมือนฟังก์ชันปกติแต่มีค่าใช้จ่ายเพิ่ม อย่าใช้ suspend โดยไม่จำเป็น

จะดีบักฟังก์ชัน suspend ได้อย่างไร?

Kotlin มี kotlinx-coroutines-debug — ไลบรารีที่มี DebugProbes และเครื่องมือติดตามโครูทีน ใน Android Studio ตั้งแต่ Arctic Fox มีแท็บ Coroutines ในตัวในดีบักเกอร์ที่แสดงโครูทีนที่ทำงานอยู่ สถานะ และจุดหยุดชั่วคราว

จำนวนจุดหยุดชั่วคราวส่งผลต่อประสิทธิภาพหรือไม่?

แต่ละจุดหยุดชั่วคราวสร้างสถานะใหม่ในเครื่องสถานะ สำหรับแอปพลิเคชันส่วนใหญ่ ค่าใช้จ่ายของหนึ่งจุดคือไม่กี่นาโนวินาที (Kotlin 1.9+) เฉพาะเมื่อมีจุดนับหมื่นในลูป คุณควรพิจารณารวมการดำเนินการหรือใช้ sequence/flow

ฟังก์ชัน suspend แตกต่างจาก async/await ในภาษาอื่นอย่างไร?

ใน Kotlin suspend เป็นตัวปรับชนิดฟังก์ชัน ไม่ใช่ตัวบอกค่าที่คืน (เช่น async ใน C#) ฟังก์ชัน suspend ใด ๆ สามารถมีพารามิเตอร์และชนิดที่คืนใด ๆ และการเรียกของมันในทางไวยากรณ์ไม่แตกต่างจากการเรียกฟังก์ชันปกติ — ไม่มีตัวดำเนินการ await ที่ตำแหน่งที่เรียก

จะแปลงฟังก์ชัน callback เป็น suspend ได้อย่างไร?

ใช้ suspendCancellableCoroutine สำหรับการปรับเปลี่ยน ภายใน คุณลงทะเบียน callback ที่เรียก continuation.resume() และคืนโทเคนการยกเลิกถ้า callback รองรับการยกเลิกการสมัครสมาชิก นี่เป็นรูปแบบมาตรฐานสำหรับครอบคลุม Android API เก่า

สรุป

  • Suspend function — ฟังก์ชันที่มีตัวปรับ suspend ซึ่งสามารถหยุดการทำงานชั่วคราวโดยไม่บล็อกเธรดผ่านกลไก Continuation
  • เครื่องสถานะ — การแสดงภายในของฟังก์ชัน suspend ในไบต์โค้ด Kotlin ที่แต่ละจุดหยุดชั่วคราวเป็นสถานะแยกต่างหากพร้อมป้ายกำกับ
  • Continuation — พารามิเตอร์ที่ซ่อนอยู่ซึ่งมีบริบทของโครูทีนและเมธอด resumeWith สำหรับกลับมาทำงานต่อ
  • เรียกจากโครูทีนเท่านั้น — ฟังก์ชัน suspend ไม่สามารถเข้าถึงได้จากฟังก์ชันปกติโดยไม่มี runBlocking หรือ CoroutineScope
  • การดำเนินการแบบบล็อก ภายใน suspend ต้องใช้ withContext(Dispatchers.IO) — มิฉะนั้นเธรดจะถูกบล็อก
  • Room และ Retrofit รองรับฟังก์ชัน suspend โดยกำเนิด จัดการเธรดพื้นหลังโดยอัตโนมัติ
  • CancellationException — จัดการการยกเลิกผ่าน finally + NonCancellable อย่าจับ CancellationException โดยไม่คิด

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม