У kotlinx.coroutines
бібліотеці ви можете запустити нову програму, використовуючи або launch
(з join
), або async
(з await
). У чому різниця між ними?
У kotlinx.coroutines
бібліотеці ви можете запустити нову програму, використовуючи або launch
(з join
), або async
(з await
). У чому різниця між ними?
Відповіді:
launch
використовується для стрілянини та забуття програми . Це як почати нову нитку. Якщо код всередині launch
закінчується за винятком, то він розглядається як невиконаний виняток у потоці - зазвичай друкується на stderr у додатках JVM та виходить з ладу для програм Android. join
використовується для очікування завершення запущеної програми та не поширює її виняток. Однак, збій дочірньої програми скасовує свого батьківського з відповідним винятком.
async
використовується для запуску програми, яка обчислює певний результат . Результат представлений екземпляром, Deferred
і ви повинні використовувати await
його. Невиконане виключення всередині async
коду зберігається всередині отриманого результату Deferred
і не надходить ніде більше, воно буде беззвучно відкинуто, якщо не буде оброблено. Ви НЕ МОЖЕТЕ забути про програму, яку ви розпочали з асинхронізації .
Цей посібник https://github.com/Kotlin/kotlinx.coroutines/blob/master/coroutines-guide.md вважаю корисним. Я цитую істотні частини
🦄 сопрограммная
По суті, супроводи - це легкі нитки.
Таким чином, ви можете думати про програму як про щось, що керує потоком дуже ефективно.
🐤 запуск
fun main(args: Array<String>) {
launch { // launch new coroutine in background and continue
delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
println("World!") // print after delay
}
println("Hello,") // main thread continues while coroutine is delayed
Thread.sleep(2000L) // block main thread for 2 seconds to keep JVM alive
}
Так launch
запускається фонова нитка, щось робиться і негайно повертає маркер як Job
. Ви можете закликати join
це Job
блокувати, поки ця launch
нитка не завершиться
fun main(args: Array<String>) = runBlocking<Unit> {
val job = launch { // launch new coroutine and keep a reference to its Job
delay(1000L)
println("World!")
}
println("Hello,")
job.join() // wait until child coroutine completes
}
🦆 асинхронізація
Концептуально асинхрон - це як запуск. Він починає окрему програму, яка є легкою ниткою, яка працює одночасно з усіма іншими процедурами. Різниця полягає в тому, що запуск повертає роботу і не несе жодного результату, а async повертає відкладене - невимушене майбутнє, що не блокує, що представляє обіцянку отримати результат пізніше.
Так async
запускається фонова нитка, щось робиться і негайно повертає маркер як Deferred
.
fun main(args: Array<String>) = runBlocking<Unit> {
val time = measureTimeMillis {
val one = async { doSomethingUsefulOne() }
val two = async { doSomethingUsefulTwo() }
println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")
}
Ви можете використовувати .await () на відкладене значення, щоб отримати його кінцевий результат, але відкладене - це також робота, тож ви можете скасувати його, якщо потрібно.
Так Deferred
насправді є Job
. Дивіться https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.experimental/-deferred/index.html
interface Deferred<out T> : Job (source)
🦋 async є нетерплячим за замовчуванням
Існує можливість лінь асинхронізувати за допомогою необов'язкового параметра запуску зі значенням CoroutineStart.LAZY. Він запускає програму лише тоді, коли її очікують потрібні очікувані або якщо викликається функція запуску.
launch
і async
використовуються для початку нових процедур. Але вони виконують їх по-різному.
Я хотів би показати дуже базовий приклад, який допоможе вам зрозуміти різницю дуже легко
- запуск
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnCount.setOnClickListener {
pgBar.visibility = View.VISIBLE
CoroutineScope(Dispatchers.Main).launch {
val currentMillis = System.currentTimeMillis()
val retVal1 = downloadTask1()
val retVal2 = downloadTask2()
val retVal3 = downloadTask3()
Toast.makeText(applicationContext, "All tasks downloaded! ${retVal1}, ${retVal2}, ${retVal3} in ${(System.currentTimeMillis() - currentMillis)/1000} seconds", Toast.LENGTH_LONG).show();
pgBar.visibility = View.GONE
}
}
// Task 1 will take 5 seconds to complete download
private suspend fun downloadTask1() : String {
kotlinx.coroutines.delay(5000);
return "Complete";
}
// Task 1 will take 8 seconds to complete download
private suspend fun downloadTask2() : Int {
kotlinx.coroutines.delay(8000);
return 100;
}
// Task 1 will take 5 seconds to complete download
private suspend fun downloadTask3() : Float {
kotlinx.coroutines.delay(5000);
return 4.0f;
}
}
У цьому прикладі мій код завантажує 3 дані одним натисненням btnCount
кнопки та показує pgBar
панель прогресу, поки не завершиться завантаження. Є 3 suspend
функції downloadTask1()
, downloadTask2()
і downloadTask3()
які завантажують дані. Для їх моделювання я використовував delay()
ці функції. Ці функції чекає 5 seconds
, 8 seconds
і 5 seconds
відповідно.
Як ми використовували launch
для запуску цих функцій призупинення, launch
виконуватимемо їх послідовно (по одному) . Це означає, downloadTask2()
що розпочнеться після downloadTask1()
завершення і downloadTask3()
розпочнеться лише після downloadTask2()
завершення.
Як і в вихідному екрані Toast
, загальний час виконання , щоб завершити всі 3 завантаження призведе до 5 секундам + 8 секунд + 5 секунд = 18 секунд зlaunch
- асинхронізація
Як ми бачили, це launch
робить виконання sequentially
для всіх 3 завдань. Час на виконання всіх завдань був 18 seconds
.
Якщо ці завдання незалежні і якщо вони не потребують результату обчислення інших завдань, ми можемо змусити їх виконуватись concurrently
. Вони б почалися одночасно і бігали одночасно у фоновому режимі. Це можна зробити за допомогою async
.
async
повертає екземпляр Deffered<T>
типу, де T
це тип даних, який повертає наша функція призупинення. Наприклад,
downloadTask1()
повернеться Deferred<String>
як String - це тип повернення функціїdownloadTask2()
повернеться Deferred<Int>
як Int - це тип повернення функціїdownloadTask3()
повернеться, Deferred<Float>
оскільки Float - це тип повернення функціїМи можемо використовувати об'єкт повернення від async
типу, Deferred<T>
щоб отримати повернене значення у T
тип. Це можна зробити за допомогою await()
дзвінка. Наприклад, перевірте код нижче
btnCount.setOnClickListener {
pgBar.visibility = View.VISIBLE
CoroutineScope(Dispatchers.Main).launch {
val currentMillis = System.currentTimeMillis()
val retVal1 = async(Dispatchers.IO) { downloadTask1() }
val retVal2 = async(Dispatchers.IO) { downloadTask2() }
val retVal3 = async(Dispatchers.IO) { downloadTask3() }
Toast.makeText(applicationContext, "All tasks downloaded! ${retVal1.await()}, ${retVal2.await()}, ${retVal3.await()} in ${(System.currentTimeMillis() - currentMillis)/1000} seconds", Toast.LENGTH_LONG).show();
pgBar.visibility = View.GONE
}
Таким чином, ми запустили всі 3 завдання одночасно. Отже, мій загальний час виконання був би лише тим, на 8 seconds
який час, downloadTask2()
оскільки він найбільший з усіх трьох завдань. Це можна побачити на наступному скріншоті вToast message
launch
async
launch
і async
інше розпочнуть нові спроби. Ви порівнюєте одну програму, яка не має дітей, і одну програму з 3 дітьми. Ви можете замінити кожну з async
викликів launch
і абсолютно нічого не зміниться щодо одночасності.
обидва конструктори програм, а саме запуск та асинхронізація - це в основному лямбда з приймачем типу CoroutineScope, що означає, що їх внутрішній блок складається як функція призупинення, отже вони обидва працюють в асинхронному режимі, і вони обидва будуть виконувати свій блок послідовно.
Різниця між запуском і асинхронією полягає в тому, що вони дають дві різні можливості. Конструктор запуску повертає Job, однак функція async поверне об'єкт Deferred. Ви можете використовувати запуск для виконання блоку, який ви не очікуєте від нього жодного поверненого значення, тобто запис у базу даних або збереження файлу або обробка чогось, в основному, лише викликав його побічний ефект. З іншого боку, async, який повертає відкладене, як я вже говорив, повертає корисне значення при виконанні його блоку - об'єкта, який обертає ваші дані, тому ви можете використовувати його в основному для його результату, але можливо і для його побічного ефекту. Примітка: ви можете зняти відкладений і отримати його значення за допомогою функції await, яка блокує виконання ваших операторів, поки значення не повернеться або не буде викинуто виняток!
обидва програми для створення програм (запуск та асинхронізація) скасовуються.
що-небудь більше?: так з запуском, якщо виняток буде кинуто в його блок, програма автоматично скасовується і винятки доставляються. З іншого боку, якщо це трапляється з асинхронізацією, виняток не поширюється далі, а його слід спіймати / обробляти у поверненому об'єкті Відкладено
детальніше про супроводи https://kotlinlang.org/docs/tutorials/coroutines/coroutines-basic-jvm.html https://www.codementor.io/blog/kotlin-coroutines-6n53p8cbn1
запуск повертає роботу
async повертає результат (відкладене завдання)
запуск із приєднанням використовується для очікування, коли робота закінчиться. Він просто призупиняє виклик програми, що викликає join (), залишаючи поточний потік вільним для виконання іншої роботи (наприклад, виконання іншої програми) тим часом.
async використовується для обчислення деяких результатів. Він створює підпрограму і повертає її майбутній результат як реалізацію відкладеного. Запущена коренева програма скасовується, коли отримане відкладене скасовується.
Розглянемо метод асинхронізації, який повертає значення рядка. Якщо метод асинхронізації використовується без очікування, він поверне рядок "Відкладений", але якщо використовується "очікування", ви отримаєте рядок як результат
Ключова різниця між асинхронізацією та запуском. Відкладене повертає певне значення типу T після завершення виконання програми Coroutine, тоді як робота не виконує.
Async vs Launch Async vs Launch Diff Image
запуск / асинхронізація не має результату
асинхронізація результату