launch 適合處理沒有回傳值的非同步任務,但實務上我們也常常需要從非同步任務取回結果,這時就輪到 async 上場。async 同樣會建立一個新的 Coroutine,差別在於它的回傳型別是 Deferred,而 Deferred 是會帶結果的 Job。
async 與 await#
async 用來建立會回傳值的 Coroutine。透過 await() 等待結果,回傳值就是區塊裡的最後一行(或 return@async 指定的值)。
fun main() = runBlocking {
val deferred = async {
println("async start")
delay(500)
println("async end")
50
}
val deferredValue = deferred.await()
println("done $deferredValue")
}把 async 的回傳值存進 deferred。當外層程式呼叫 await() 時,會在這裡等到 async 區塊跑完,再把結果傳出去。
Kotlin 的 lambda 最後一行就是它的回傳值,可以省略
return。如果想顯式宣告,也可以寫成return@async 50。Kotlin 會自動推斷回傳型別,IDE 通常會建議移除手動加上的型別宣告。
await 與 join 的差異#
await() 與 join() 都會等到對應的 Coroutine 完成才繼續往下走,差別在於:
join()沒有回傳值,只等任務結束。await()等任務結束後會回傳 async 區塊的值(如上例的50)。
兩者都會在「Coroutine 完成或拋出例外」時結束等待。
沒有呼叫 await 會怎樣#
async 與 launch 一樣,建立後就會被立刻排程執行,並不會因為沒人呼叫 await() 就停在那裡。少了 await(),只是不會去取結果而已:
fun main() = runBlocking {
async {
println("async start")
delay(500)
println("async end")
return@async 50
}
println("done")
}執行順序就跟用 launch 寫一樣,外層的 done 會先被印出。
延遲啟動:CoroutineStart.LAZY#
跟 launch 相同,async 也支援用 CoroutineStart.LAZY 把 Coroutine 設成延遲啟動。差別是 async 比 launch 多了一個啟動方式 await(),因為 async 本來就會回傳值:
await():啟動並等待結果。start():只啟動,不取結果。join():啟動並等待完成,但不取結果。
其中只有 await() 會帶回 Deferred 的回傳值。
launch 與 async 的對照#
| Builder | 回傳型別 | 適用場景 | 取得結果的方式 |
|---|---|---|---|
| launch | Job | 沒有回傳值的非同步任務 | 不適用 |
| async | Deferred | 有回傳值的非同步任務 | await() |
兩者本質上都是可被取消的背景任務。Job 與 Deferred 共有的方法包含 cancel()、start()、join();Deferred 額外多了 await() 用來取得結果。
小結#
- async 用來處理「需要回傳值」的非同步任務,回傳的是 Deferred。
await()會等待 async 完成並把結果傳出來;如果不需要結果,async 也會像 launch 一樣被排程執行。- 加上
CoroutineStart.LAZY之後,async 同樣可以延後啟動,並用await()、start()或join()喚醒。
原文出處#
- 原書/iThome:https://ithelp.ithome.com.tw/articles/10262897