async: มันคืออะไร, Deferred และโครอูตินแบบขนานานทำงานอย่างไร

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

async — คือ Coroutine Builder ใน Kotlin ที่เริ่มแล่น coroutine และส่งคืนอ็อบเจ็กต์ Deferred<T> เพื่อรับผลลัพธ์ของการทำงานแบบไม่ชิงครอบครัง async ช่วยให้สามารถทำงานหลายอย่างแบบขนานานและรวมผลลัพธ์ผ่าน await() ตาม คู่มือ JetBrains, 2024 async เป็นวิธีที่ต้องการในการจัดระเบียบการคำนวณแบบขนานานใน coroutine

หัวข้อสำคัญ

  • async — ตัวสร้าง coroutine ที่ส่งคืน Deferred<T> พร้อมผลลัพธ์ของการทำงานแบบไม่ชิงครอบครัง
  • Deferred — คำมั่นสัญญาแบบไม่ชิงครอบครัง, สืบทอด Job, มีเมธอด await() สำหรับรับผลลัพธ์
  • async ช่วยให้ทำงานอิสระแบบขนานาน, ลดเวลาโดยรวม
  • await() — ฟังก์ชัน suspend ที่หยุด coroutine ชั่วคราวศาหรือจนกว่าจะได้รับผลลัพธ์โดยไม่บล็อก
  • การทำงานพร้อมกันแบบมีโครงสร้าง กับ async รับประกันการยกเลิกงานแบบขนานานทั้งหมดเมื่อเกิดข้อผิดพลาด

async ใน Kotlin Coroutines คืออะไร?

async — เป็นฟังก์ชันส่วนขยายของ CoroutineScope ที่สร้าง coroutine และส่งคืนผลลัพธ์ ต่างจาก launch, async ส่งคืน Deferred<T> — อ็อบเจ็กต์ที่แทนค่าในอนาคต Coroutine ที่เริ่มผ่าน async ทำงานพร้อมกับ coroutine อื่น ช่วยให้สามารถทำงานคำนวณแบบขนานานได้

async ถูกใช้เมื่อต้องการผลลัพธ์ของการทำงานแบบไม่ชิงครอบครังสำหรับการประมวลผลต่อ: โหลดข้อมูลจากหลายแหล่ง, การประมวลผลทีละชุดของคอแลกชัน, การร้องขอแบบขนานานไปยัง API ต่าง ๆ Deferred ถูกสร้างทันที และผลลัพธ์จะพร้อมใช้งานหลังจาก coroutine สิ้นสุด

kotlin
import kotlinx.coroutines.*

suspend fun String.delayedValue(): String {
    delay(1000L)
    return this
}

fun main() = runBlocking {
    val deferred: Deferred<String> = async { "Hello".delayedValue() }
    println(deferred.await())
}

ไวยาการณ์ async และ Deferred

ลายเซ็นของ async คล้ายกับ launch: พารามิเตอร์ CoroutineContext, CoroutineStart เหมือนกัน และบล็อก suspend ความแตกต่างอยู่ที่ชนิดของค่าที่ส่งคืน — Deferred<T> แทน Job, โดย T คือชนิดของผลลัพธ์ที่ส่งคืนโดยนิพจน์สุดท้ายของบล็อก

kotlin
public fun <T> CoroutineScope.async(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> T
): Deferred<T>

Deferred<T> — อินเตอร์เฟสที่สืบทอด Job และเพิ่มเมธอด await(), getCompleted(), getCompletionExceptionOrNull() await() เป็นฟังก์ชัน suspend และสามารถเรียกได้ภายใน coroutine หรือฟังก์ชัน suspend อื่น getCompleted() เป็นเมธอดที่ไม่บล็อก ซึ่งจะเกิดข้อยกเว้นหาก coroutine ยังไม่สิ้นสุด

งานแบบขนานานกับ async

ข้อดีหลักของ async คือความสามารถในการทำงานแบบขนานาน แทนที่จะรอแต่ละการทำงานเรียงลำดับ (เวลารวม = ผลรวมของเวลา), async เริ่ม coroutine พร้อมกัน ทำให้เวลารวมลดลงเป็นเวลาของ การทำงานที่ยาวที่สุด

การโหลดข้อมูลแบบขนานาน

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

kotlin
suspend fun loadUserDashboard(userId: String): Dashboard = coroutineScope {
    val profile = async { api.getProfile(userId) }
    val settings = async { api.getSettings(userId) }
    val orders = async { api.getRecentOrders(userId) }

    Dashboard(
        profile = profile.await(),
        settings = settings.await(),
        orders = orders.await()
    )
}

ฟังก์ชัน coroutineScope เริ่ม coroutine ย่อย และรอให้มันสิ้นสุดก่อนจะส่งคืนผลลัพธ์ บล็อกทั้งหมดทำงานแบบขนานาน, ไม่ใช่เรียงลำดับ

การจัดการผลลัพธ์ Deferred

Deferred รองรับหลายเมธอดในการทำงานกับผลลัพธ์ นอกเหนือจาก await() แล้ว ยังมีเมธอดสำหรับตรวจสอบสถานะ, รับค่าที่สิ้นสุด และรวมหลาย Deferred ผ่านฟังก์ชันอำนวยความสะดวกของ Kotlin Coroutines

เมธอดคำอธิบายSuspend?
await()หยุด coroutine ชั่วคราวจนกว่าจะได้รับผลลัพธ์ใช่
getCompleted()ส่งคืนผลลัพธ์หาก coroutine สิ้นสุดแล้ว (มิฉะนั้น Exception)ไม่
getCompletionExceptionOrNull()ส่งคืนข้อยกเว้นเมื่อเกิดข้อผิดพลาดหรือ nullไม่
isCompletedตรวจสอบว่า coroutine สิ้นสุดแล้วหรือไม่ไม่

สำหรับการรวมรายการ Deferred awaitAll() ถูกใช้ — ฟังก์ชันที่หยุดการทำงานไว้ชั่วคราวจนกว่า Deferred ทั้งหมดในคอแลกชันจะสิ้นสุด หากอย่างน้อยหนึ่งรายการสิ้นสุดด้วยข้อผิดพลาด awaitAll() จะเกิดข้อยกเว้น

kotlin
suspend fun <T> loadAll(requests: List<suspend () -> T>): List<T> {
    return coroutineScope {
        val deferreds = requests.map { async { it() } }
        deferreds.awaitAll()
    }
}

ความแตกต่างระหว่าง async และ launch

การเลือกระหว่าง async และ launch ขึ้นอยู่กับว่าต้องการผลลัพธ์ของ coroutine หรือไม่ launch ส่งคืน Job และเหมาะสำหรับงาน fire-and-forget async ส่งคืน Deferred และใช้สำหรับงานที่ส่งคืนข้อมูล ในแง่ของไวยาการณ์และพารามิเตอร์ ตัวสร้างทั้งสองเหมือนกัน

  • launch → Job (ไม่มีผลลัพธ์), async → Deferred<T> (ผลลัพธ์ T)
  • launch — งาน fire-and-forget (การบันทึก, แคปช์, การส่งข้อมูลวิเคราะห์)
  • async — การคำนวณแบบขนานาน (โหลดจากหลาย API, การประมวลผลทีละชุด)
  • ทั้งสองรองรับ การทำงานพร้อมกันแบบมีโครงสร้าง และพารามิเตอร์ CoroutineStart
  • await() ใช้ได้เฉพาะ Deferred, launch ไม่ให้ผลลัพธ์

กฎที่สำคัญ: อย่าใช้ async สำหรับ fire-and-forget หากไม่ต้องการผลลัพธ์ของ coroutine ให้ใช้ launch async สร้างโอเวอร์เฮดของการสร้าง Deferred ซึ่งไม่สมเหตุสมผลในสถานการณ์ดังกล่าว

ตัวอย่าง async ในการพัฒนามือถือ

ใน Android, async ถูกใช้สำหรับการดำเนินการแบบขนานานภายใน ViewModel และ UseCases สถานการณ์ทั่วไป: โหลดโปรไฟล์และฟีดข่ଲวพร้อมกัน, รับข้อมูลสภาพอากาศจากหลายบริการสภาพอากาศ, การซิงโครไนซ์ข้อมูลแบบแบตช์กับเซิร์ฟเวอร์

kotlin
class HomeViewModel : ViewModel() {
    fun loadHomeScreen() {
        viewModelScope.launch {
            val userData = async(Dispatchers.IO) { repository.getUserData() }
            val newsFeed = async(Dispatchers.IO) { repository.getNewsFeed() }
            val notifications = async(Dispatchers.IO) { repository.getNotifications() }

            val state = HomeState(
                user = userData.await(),
                news = newsFeed.await(),
                unread = notifications.await()
            )
            _uiState.update { state }
        }
    }
}

แนะนำให้ห่อกลุ่ม async ใน coroutineScope หรือ supervisorScope เพื่อแยกข้อผิดพลาด supervisorScope ช่วยให้ coroutine async อื่นสามารถทำงานต่อไปได้แม้ว่าหนึ่งจะล้มเหลว — มีประโยชน์สำหรับงานที่ไม่สำคัญ

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

สามารถใช้ async โดยไม่ต้อง await ได้หรือไม่?

ได้ แต่ไม่มีความหมาย async โดยไม่มี await() เริ่ม coroutine แต่ผลลัพธ์ของมันจะหายไป หากไม่ต้องการผลลัพธ์ ให้ใช้ launch — มันไม่สร้าง Deferred และมีประสิทธิภาพด้านหน่วยความจำและประสิทธิภาพมากกว่า

async จัดการกับข้อยกเว้นอย่างไร?

ข้อยกเว้นภายใน async จะถูกเก็บไว้ใน Deferred การเรียก await() จะเกิดข้อยกเว้นนี้ หากต้องการตรวจสอบโดยไม่เกิดข้อยกเว้น ให้ใช้ getCompletionExceptionOrNull() เมื่อใช้ coroutineScope ข้อผิดพลาดจะยกเลิก coroutine ย่อยทั้งหมด

awaitAll ใน Kotlin Coroutines คืออะไร?

awaitAll() — ฟังก์ชันส่วนขยายสำหรับ Iterable<Deferred<T>> ที่หยุด coroutine ชั่วคราวจนกว่า Deferred ทั้งหมดจะสิ้นสุด ส่งคืนรายการผลลัพธ์ หากอย่างน้อยหนึ่ง Deferred ล้มเหลว มันจะเกิดข้อยกเว้นและยกเลิกส่วนที่เหลือ

async แตกต่างจาก GlobalScope.async อย่างไร?

ใช่ async เป็นฟังก์ชันส่วนขยายของ CoroutineScope ที่ผูกกับขอบเขต GlobalScope.async สร้าง coroutine โดยไม่ผูกกับวงจรของชีวิต — มันสามารถทำงานได้อย่างไม่จำกัด GlobalScope ไม่ได้รับคำแนะนำใน Android เนื่องจากความเสี่ยงของการรั่วไหล

สามารถเริ่ม coroutine async ได้กี่ตัวสำหรับคำขอแบบขนานาน?

จำนวนจะถูกกำหนดโดยตัวจัดการ: Dispatchers.IO รองรับได้สูงสุด 64 เธรดพร้อมกัน, Dispatchers.Default — สูงสุดตามจำนวนแกน CPU สำหรับงานจำนวนมาก ให้ใช้การทำงานแบบขนานานแบบจำกัดผ่าน Semaphore หรือ mapNotNull กับ awaitAll

สรุป

  • async — ตัวสร้าง coroutine ที่ส่งคืน Deferred<T> สำหรับการรับผลลัพธ์แบบไม่ชิงครอบครัง
  • Deferred สืบทอด Job และเพิ่มเมธอด await(), getCompleted(), getCompletionExceptionOrNull()
  • การทำงานแบบขนานาน กับ async ช่วยลดเวลารวมลงเป็นเวลาของการทำงานที่ยาวที่สุด
  • awaitAll() — เครื่องมือสำหรับรอ Deferred ทั้งหมดในคอแลกชัน
  • coroutineScope — ตัวห่อที่แนะนำสำหรับกลุ่ม async พร้อมการรับประกันการยกเลิก
  • อย่าใช้ async สำหรับ fire-and-forget — ให้ใช้ launch แทน
  • ใน Android, async ถูกใช้สำหรับการโหลดข้อมูลแบบขนานานใน ViewModel กับ viewModelScope

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

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

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

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