coroutine神奇又好用,那我要怎麼開始呢?
官方提供了兩種方法,launch和 async
launch的意思,大概是我要這裡創造新的coroutine,並在指定的Thread之中運行,這裡是哪裡呢?
只要是coroutinescope裡面,或是suspend function都可以(因為suspend一定要在coroutineScope或suspend function裡面執行)
而launch的特性是他不會在coroutine結束時返回值,多數情況都會以launch創建coroutine
start a new coroutine that is “fire and forget” — that means it won’t return the result to the caller.
val scope = CoroutineScope(rootJob)
scope.launch ( Dispatchers.IO ){
//code
}
//看原始碼,我們寫在coroutine裡的代碼塊會被標記上suspend
fun CoroutineScope.launch {
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
}
那async要怎麼用?
當兩個任務之間沒有相互關係時,async併發可以讓我們減少等待執行的時間,並回傳deferred類型,因為無法確定非同步任務會在何時結束,不論是運算結果或是Exception,他都預期開發者會呼叫await()掛起函數,以獲得回傳值或是錯誤訊息,所以預設並不會丟出exception
val scope = CoroutineScope(rootJob)
scope.async {
}.await()
可以比較一下原始碼
fun CoroutineScope.async() {
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
}: Deferred<T>
只看文字應該會覺得似懂非懂,這裡我們再帶個小範例
val task: (String) ->Unit = { Timber.d("longTimeTask in $it") }
fun longTimeTask(): (String) -> Unit {
return task
}
val scope = CoroutineScope(rootJob)
val notRelateWork1 = scope.async {
Timber.d("in async 1")
longTimeTask()
}
val notRelateWork2 = scope.async {
longTimeTask().invoke("async 2")
Timber.d("in async 2")
}
scope.launch {
Timber.d("in launch")
notRelateWork1.await().invoke("async 1") //(String) -> Unit
notRelateWork2.await() //Unit
}
//in async 1
//longTimeTask in async 2
//in async 2
//in launch
//longTimeTask in async 1
這裡的順序非常重要,儘管我們需要呼叫await,來拿到async回傳參數,但在async回傳值之前的代碼會先被執行,而在async裡面默認是回傳最後一行的值,所以我搭配lambda,大家應該很容易就能看出呼叫執行task的地方差異了
重點:launch和async很大的差別在於
Warning: A big difference between launch and async is how they handle exceptions. async expects that you will eventually call await to get a result (or exception) so it won’t throw exceptions by default. That means if you use async to start a new coroutine it will silently drop exceptions.
我們介紹完了await()和async的基本知識,但剛剛提過 當兩個任務之間沒有相互關係時,async併發可以讓我們減少等待執行的時間, 具體來說,還是看code比較實際
今天如果用launch要等兩秒,但async併發只要等1秒多一點,今天如果是有10個任務,launch要等10秒,但async一樣是一秒多
suspend delay1000():Int{
delay(1000)
//pretend a api call
return 1
}
val time = measureTimeMillis {
val one = async { delay1000() }
val two = async { delay1000() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
//或分開寫
val time = measureTimeMillis {
println("The answer is ${concurrentSum()}")
}
println("Completed in $time ms")
suspend fun concurrentSum(): Int = coroutineScope {
val one = async { delay1000() }
val two = async { delay1000() }
one.await() + two.await()
}
我實在找不到圖說明這個步驟,用文字解釋就是在delay1000(),裡面的delay是suspend function,在launch裡面時,suspend會依順序觸發,而在async代碼執行到那裡可被掛起,讓thread可以先執行其他任務,當delay()結束後,會切回delay後面,繼續執行其他代碼
剛才講到了代碼的執行順序,但如果我們希望在呼叫的時候才執行,有沒有像 kotlin lazy的方法呢?有,也是lazy
我覺得很清楚了,應該不用解釋吧
launch(start = CoroutineStart.LAZY) { delay1000() }
惰性的併發
只有通过 await 或者在 Job 的 start 函数调用的时候协程才会启动
val time = measureTimeMillis {
val one = async(start = CoroutineStart.LAZY) { delay1000() }
val two = async(start = CoroutineStart.LAZY) { delay1000() }
// 执行一些计算
one.start() // 启动第一个
two.start() // 启动第二个
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
要注意,如果不用start只用await,async coroutine會依序啟動,而不會變成併發啟動
當我們開啟一個coroutine後,他就是 thread嗎?
不對不對,我們之前講過,kotlin 的coroutine只是一個線程框架,他是將線程包裝成方便的版本,launch不是 thread,Dispatcher也不是thread,而這段Code實際上會是這樣
val scope = CoroutineScope(rootJob)
scope.launch ( Dispatchers.IO ){
//code
}
//變成
handler.post{
//code from launch block
//launch裡面的代碼
}
launch和Dispatcher都不是thread本身,他們是將任務透過handler post到 thread 的looper裡面。
參考連結:
必看
中文文檔
android文檔
英文async文檔
github async 文檔
中秋節來個應景梗圖,中秋節快樂
放假太爽,day 5打成day 4,回來更新
您好 我按照你的步驟但有些疑問想請教您
當我註解了work2 但實際上 work2內容仍然被執行了,請問這是什麼原因呢 ?
scope.launch {
Log.d("longTimeTask", "Start Launch")
work1.await().invoke("I am task One")
// work2.await()
}
private val work1 = scope.async {
Log.d("longTimeTask", "async work1 ")
longTimeTask()
}
private val work2 = scope.async {
longTimeTask().invoke("async 2 invoke")
Log.d("longTimeTask", "async work2 ")
}
※console
D/longTimeTask: async work1
D/longTimeTask: From task async 2 invoke
D/longTimeTask: async work2
D/longTimeTask: Start Launch
D/longTimeTask: From task I am task One
哈囉,因為在生成 work2 變數的時候,就已經在 scope 之下,用 async 產出了一個 childCoroutineScope,而 async 的特性是並行,並在 await 回傳
換句話說, async 裡面的程式碼會在執行緒有空閒的時候執行,如果任務先結束了,會留一個 Deffered 類型的資料,等到有人呼叫 await 的時候,可以立刻回傳值,也就是說 async 裡面的程式碼,即使沒有被 await 也會執行,套件設計上預期開發者會記得呼叫 await,而 ide 好像也會提醒
不好意思我不太懂您這段意思
今天如果用launch要等兩秒,但async併發只要等1秒多一點,今天如果是有10個任務,launch要等10秒,但async一樣是一秒多
在我看來launch 開十個也還是一秒多
有差距的只是async 是defer , launch是job吧?