← Назад к вопросам
Как дождаться результата выполнения двух параллельных задач
2.0 Middle🔥 222 комментариев
#Многопоточность и асинхронность
Комментарии (2)
🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Ожидание результатов параллельных задач в Android/Kotlin
В Android-разработке на Kotlin существует несколько основных подходов для ожидания результатов выполнения двух или более параллельных задач. Выбор конкретного метода зависит от контекста выполнения (UI-поток или фоновый поток) и используемых механизмов асинхронности.
Основные подходы
1. Использование async/await в Kotlin Coroutines
Наиболее современный и рекомендуемый способ для фоновых операций:
import kotlinx.coroutines.*
suspend fun fetchParallelData(): Pair<Result1, Result2> = coroutineScope {
val deferred1 = async { fetchDataFromSource1() }
val deferred2 = async { fetchDataFromSource2() }
// Ожидаем завершения обеих задач
val result1 = deferred1.await()
val result2 = deferred2.await()
return@coroutineScope Pair(result1, result2)
}
// Использование
viewModelScope.launch {
try {
val (data1, data2) = fetchParallelData()
// Обработка результатов
} catch (e: Exception) {
// Обработка ошибок
}
}
Ключевые особенности:
- Обе задачи запускаются параллельно
await()приостанавливает корутину, не блокируя поток- Автоматическая отмена при отмене родительской корутины
2. Комбинация async с таймаутом
suspend fun fetchWithTimeout(): Pair<Result1?, Result2?> = withTimeoutOrNull(5000) {
coroutineScope {
val deferred1 = async { fetchData1() }
val deferred2 = async { fetchData2() }
try {
val result1 = deferred1.await()
val result2 = deferred2.await()
Pair(result1, result2)
} catch (e: CancellationException) {
null // Таймаут или отмена
}
}
}
3. Использование awaitAll() для нескольких задач
suspend fun fetchMultipleSources(): List<Any> = coroutineScope {
val deferredTasks = listOf(
async { fetchFromAPI1() },
async { fetchFromAPI2() },
async { fetchFromDatabase() }
)
deferredTasks.awaitAll() // Ждем все результаты
}
Альтернативные подходы
4. RxJava - оператор zip()
Single.zip(
apiService.getData1().subscribeOn(Schedulers.io()),
apiService.getData2().subscribeOn(Schedulers.io()),
BiFunction { result1, result2 ->
Pair(result1, result2)
}
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({ pair ->
// Обработка результатов
}, { error ->
// Обработка ошибки
})
5. Kotlin Flow - combine() или zip()
val flow1 = flow { emit(fetchData1()) }
val flow2 = flow { emit(fetchData2()) }
flow1.zip(flow2) { data1, data2 ->
Pair(data1, data2)
}.flowOn(Dispatchers.IO)
.collect { pair ->
// Получение объединенного результата
}
6. Колбэки с счетчиком (legacy-подход)
fun fetchBoth(callback: (Result1?, Result2?) -> Unit) {
var result1: Result1? = null
var result2: Result2? = null
var completed = 0
fun checkCompletion() {
if (++completed == 2) {
callback(result1, result2)
}
}
fetchAsync1 { data ->
result1 = data
checkCompletion()
}
fetchAsync2 { data ->
result2 = data
checkCompletion()
}
}
Рекомендации по выбору подхода
- Для новых проектов используйте Kotlin Coroutines с
async/await - Для UI-взаимодействий применяйте
viewModelScopeилиlifecycleScope - При работе с существующим RxJava-кодом используйте операторы комбинирования
- Для обработки таймаутов применяйте
withTimeoutилиwithTimeoutOrNull - Для обработки ошибок каждой задачи независимо используйте
supervisorScope
Пример с обработкой ошибок
suspend fun fetchSafely(): Pair<Result1?, Result2?> = supervisorScope {
val deferred1 = async {
try { fetchData1() } catch (e: Exception) { null }
}
val deferred2 = async {
try { fetchData2() } catch (e: Exception) { null }
}
val result1 = deferred1.await()
val result2 = deferred2.await()
Pair(result1, result2)
}
Важные аспекты:
- При использовании
coroutineScopeошибка в любой задаче отменяет все остальные supervisorScopeпозволяет обрабатывать ошибки независимо- Всегда учитывайте контекст выполнения (UI или фоновый поток)
- Для CPU-интенсивных задач используйте
Dispatchers.Default - Для IO-операций применяйте
Dispatchers.IO
Выбор конкретного механизма зависит от архитектуры приложения, но в современных Android-приложениях Kotlin Coroutines являются предпочтительным выбором благодаря своей интеграции с Android Jetpack и более простой работе с жизненным циклом.