รู้จัก Kotlin Coroutines ตั้งแต่ Zero จนเป็น Hero : ตอนที่ 2

ความเดิมตอนที่แล้ว

เราได้รู้จักกับ Kotlin Coroutines กันบ้างคร่าวๆ แล้วว่ามันคืออะไรกันแน่ ข้อแตกต่างระหว่าง Synchronous และ Asynchronous สำหรับใครที่พลาดตอนแล้วไปคลิกข้างล่างโล้ดดด.. จิ้มมมม เบาๆ

เนื้อหาที่คุณจะได้เรียนรู้ในบทความนี้

  • เมื่อไรหละเราถึงควรใช้ Coroutine
  • คำศัพท์ที่เราจะได้พบเห็นเจอกันบ่อย ๆ และการใช้งาน อาทิเช่น
    launch , async , await , suspend function , deferred

ในตอนนี้เราจะค่อยๆ เริ่มพาทุกท่านเข้าสู่โลกแห่ง Coroutine กันทีละนิดเอาล่ะมาต่อกันเลยในตอนที่ 2 เล้ยย

เมื่อไรหละเราถึงควรจะต้องใช้ Coroutine

  • อะไรก็ตามที่ทำงานแบบยาววนานน เช่น การโหลดรูป , ดึงข้อมูล API หรือ ฐานข้อมูลต่างๆ ซึ่งในที่นี้บางคนอาจจะใช้ Room ให้การดึงข้อมูล ซึ่งการทำงานแบบยาวนานที่ว่านี้มันสามารถเข้าไป Block การทำงานของ Main Thread ได้เลยขึ้นอยู่กับแต่ละสถานะการณ์
  • ต้องการให้ Main Thread ของเราทำงานได้อย่างราบลื่นปราศจากสิ่งรบกวน การขัดจังหวะ

นอกจากนี้มันจากช่วยให้คุณอ่านโค้ดได้ง่ายขึ้นด้วยยนะ ไม่ต้อง Jump ไป Jump มาระหว่าง Call back อันซับซ้อน ปวดหัวใช่ไหมล่ะ

Free image license from www.pexels.com

Room : เป็นหนึ่งใน library ที่อยู่ภายใน Android Architecture Components ที่จะช่วยในการจัดการฐานข้อมูลของระบบปฏิบัติการ Android

Android Architecture Components : เป็นแหล่งรวมสุดยอด library ต่างๆ ที่ทาง Google ได้ปล่อยออกมาเพื่อช่วยเพิ่มประสิทธิภาพและลดระยะเวลาในการพัฒนาแอปพลิเคชันบน

ก่อนอื่นมารู้จักคำศัพท์ที่จะได้เจอกันหน่อย

หลังจากที่บทความอันที่แล้วเราเจอคำศัพท์ที่ไม่คุ้นเค้ยเอาซะเลย แต่มันคืออะไรกันนะ เราจะมาเล่าให้ฟังกัน

Launch : ถือเป็นจุดเริ่มต้นของ Coroutine อะไรก็ตามที่เกี่ยวกับคำสั่งของ Coroutine เราจะทำงานในนี้กัน เดี่ยวยกตัวอย่างให้เห็นภาพเช่นการอ่านไฟล์

launch {
// เริ่มต้นการขัดจังหวะขณะมีการอ่านข้อมูลแบบ asynchronously
val bytesRead = inChannel.read(buffer)
// จะทำงานจนถึงบรรทัดนี้ก็ต่อเมื่อการอ่านข้อมูลเสร็จสิ้น
...
...
processSomething(buffer, bytesRead)
// เริ่มต้นการขัดจังหวะขณะมีการเขียนข้อมูลแบบ asynchronously
outChannel.Write(buffer)
// จะทำงานจนถึงบรรทัดนี้ก็ต่อเมื่อการเขียนข้อมูลเสร็จสิ้น
...
...
outFile.close()
}

เสริมอีกนิดนอกจาก Launch ยังมีอีกสั่งที่คล้ายๆ กันในการสั่งเริ่มต้นการทำงานของ Coroutine นั่นก็คืออออ

Async : ถือเป็นอีกหนึ่งคำสั่งเริ่มต้นการทำงานของ Coroutine เอ้าา แล้วมันต่างกันยังไงกับ Launch เมื่อกี้นี้หละ ? เดี่ยวอดใจรออีกนิดเดี๋ยวเราจะเล่าให้ฟังเร็วๆ นี้แหละ

เห็นภาพกันแล้วใช่ไหมล่ะ มาต่อด้วยคำต่อไปกันเลย

Suspend : การขัดจังหวะในที่นี้จะหมายถึงฟังก์ชันที่มีการขัดจังหวะได้

suspend fun fetchFriendList() {

}

Await : เดี๋ยววววว ! หยุดรอเราก่อน

Launch กับ Async แตกต่างกันยังไง

  • Launch : เป็นการเริ่มต้นการทำงาน Coroutine แบบ*ไม่มีการคืนค่ากลับออกมา
  • Async : เป็นการเริ่มต้นการทำงาน Coroutine แบบ*มีการคืนค่ากลับออกมา ขณะที่ภายในฟังก์ชันแบบที่มีการขัดจังหวะได้ (Suspend Function) สามารถสั่งหยุดรอจนกว่าข้อมูลนั้นจะโหลดสำเร็จได้ โดยใช้คำสั่ง await() ที่กล่าวมา
    เมื่อสักครู่นั่นเอง

เอาล่ะมาดูตัวอย่างการใช้งาน suspend , async , await และ launch พระเอกของเรากัน

launch {
//ทำงานบน UI thread
doSomething()
//ใช้ dispatcher สลับ context

val deferred = async(Dispatchers.Default) {
//ตู้มม ทำงานบน Background thread แล้ว
2563 - 543

Delay(1000)
}
//กลับมาทำงานบน UI thread
print
(deferred.await())
//จะรอจนกว่า deferred ได้รับค่าจึงจะ print ออกมาจากการใช้ await()
}

เห็นชื่ออะไรที่เราตั้งแปลกๆ แว๊ปๆ ไหม “deferred” จริงๆ จะตั้งชื่ออะไรก็ได้นะแค่อยากให้เห็นเฉยๆ .. ค่อยว่ากัน เรามาไล่โค้ดกันน

จะเห็นได้ว่าในบรรทัดที่เรามีการใช้คำสั่ง print(deferred.await()) จะเป็นการสั่งให้ตัวคอมไพเลอร์ มีการหยุดรอชั่วคราว เนื่องจากตัวแปรกำลังมีการเรียกเพื่อรอผลลัพธ์ภายใต้ async() เอาล่ะอ่านมาจนถึงตรงนี้ก็พอจะรู้กันแล้ว เมื่อไรก็ตามที่เราต้องการให้ทำงานอะไรบางอย่างและต้องการรอผลลัพธ์เพื่อไปใช้งานต่อ สามารถใช้คำสั่ง await() เพื่อรอผลลัพธ์นั้นได้ ในขณะที่เราทำงานอยู่ภายใต้ async() ข้อมูลเหล่านั้นจะถูกห่อหุ้ม (wrapping) ด้วยอีก construct อีกตัวซึ่งเราจะเรียก construct ที่ห่อหุ้มข้อมูลเรานั้นเรียกว่าDeferred.โดย.โดยมันจะทำหน้าที่รอจนกว่าตัวมันเองจะได้รับข้อมูลที่มันรออยู่นั่นเองง

ก่อนจะไปในหัวข้อถัดไปอย่างที่เราได้บอกไว้กันในตอนต้นว่า เมื่อไรละเราถึงจะต้องใช้ Coroutine มาทวนกันหน่อยคือ

  • อยากจะช่วยแก้ปัญหาอะไรที่มันทำนานนน้านน (Manage long-running task)
  • อยากจะให้ Main Thread ของเราทำงานอย่างราบลื่น(Providing main-safety)

อ่านมาจนถึงตอนนี้พอจะเริ่มค่อยๆ เห็นภาพเกี่ยวกับ Kotlin Coroutine แล้ววสินะ

สำหรับในตอนถัดไปเราจะมาพูดถึงการใช้ Kotlin coroutines มาเขียนโค้ดให้มีความกระชับมากยิ่งขึ้น และดูอ่านง่ายขึ้น เห็นอะไรแว๊ป ๆ กันไหม Dispatcher.Defaultโดยครั้งหน้าเราจะมาเล่าให้ฟังว่ามันคืออะไร อดใจรออีกนิดนะ :))

--

--