要讓 suspend 函式真的跑起來,必須把它放進一個 Coroutine 範圍內執行。Kotlin Coroutine 提供了兩個常用的 Coroutine Builder:launch 與 async。本章先聚焦在沒有回傳值的場景:launch。
launch 的基本用法#
當一段非同步任務不需要回傳結果(也就是回傳 Unit),就適合用 launch 建立 Coroutine。
fun main() = runBlocking {
launch {
println("launch Start")
delay(500)
println("launch End")
}
println("Done")
}執行這段程式時,會先看到 Done,再看到 launch 區塊內的兩段輸出。原因是 launch 會建立一個新的 Coroutine,成為 runBlocking 的子 Coroutine;外層的 println("Done") 會先被執行,內層的 launch 任務之後才接著被排程。
把 launch 換成
coroutineScope { ... },輸出順序就會回到原始碼的順序。coroutineScope不會建立新的 Coroutine,而是繼承外層的 Coroutine context,所以裡外其實是同一個 Coroutine。
把區塊抽成 suspend 函式#
launch 區塊裡若包含 delay 這類 suspend 函式,把整段抽出去時,函式必須以 suspend 修飾:
suspend fun launchFun() {
println("launch Start")
delay(500)
println("launch End")
}suspend 函式只能在 CoroutineScope 中、或被另一個 suspend 函式呼叫。
用 Job 控制 Coroutine#
launch 的回傳值是 Job。Job 代表一個可以被取消的任務,呼叫 cancel() 就能停止這個 Coroutine。
cancel:主動取消任務#
fun main() = runBlocking {
val job = launch {
repeat(100_000) { index ->
println("launch Start $index")
delay(500)
println("launch End $index")
}
}
delay(1100)
job.cancel()
println("Done")
}外層 Coroutine 在 delay(1100) 後呼叫 job.cancel(),把原本要重複十萬次的任務中止,外層才繼續執行剩下的程式。
join:等待子 Coroutine 完成#
預設情況下,外層的程式會比 launch 區塊先執行。如果想反過來,先讓 launch 內的工作完成、再繼續往下走,可以使用 job.join():
fun main() = runBlocking {
val job = launch {
println("launch Start")
delay(500)
println("launch End")
}
job.join()
println("Done")
}joinAll:一次等待多個 Job#
針對多個 Job 同時做 join(),可以使用 joinAll:
fun main() = runBlocking {
val job = launch {
println("launch Start")
delay(500)
println("launch End")
}
joinAll(job)
println("Done")
}joinAll 內部其實就是把每個 Job 都呼叫 join():
public suspend fun joinAll(vararg jobs: Job): Unit = jobs.forEach { it.join() }延遲啟動:CoroutineStart.LAZY#
launch 預設一執行就會被排程啟動。如果想延後啟動,可以在 launch 時帶入 CoroutineStart.LAZY:
fun main() = runBlocking {
launch(start = CoroutineStart.LAZY) {
println("launch Start")
delay(500)
println("launch End")
}
println("Done")
}加上 CoroutineStart.LAZY 之後,這個 launch 不會立刻執行;如果一直沒有人去啟動它,整段程式會卡在等待。
要主動啟動,可以呼叫 job.start(),或用 job.join() 在等待時順帶啟動:
fun main() = runBlocking {
val job = launch(start = CoroutineStart.LAZY) {
println("launch Start")
delay(500)
println("launch End")
}
job.start()
println("Done")
}小結#
- launch 用來建立沒有回傳值的 Coroutine,回傳 Job。
- Job 提供
cancel()、start()、join()來掌控 Coroutine 的生命週期。 - 預設 launch 一被呼叫就會排程執行,若需要延後啟動,可帶入
CoroutineStart.LAZY,再透過start()或join()觸發。
原文出處#
- 原書/iThome:https://ithelp.ithome.com.tw/articles/10262897