← Назад к вопросам

Как реализовать метод который загружает асинхронную загрузку из 2 источников?

3.0 Senior🔥 122 комментариев
#Многопоточность и асинхронность#Сетевое взаимодействие

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Реализация асинхронной загрузки из двух источников

Для реализации метода, который выполняет асинхронную загрузку из двух источников, необходимо выбрать правильный подход в зависимости от требований: параллельная или последовательная загрузка, обработка ошибок и тип возвращаемых данных. Рассмотрим основные стратегии с использованием Kotlin Coroutines и Kotlin Flow.

Основные подходы

1. Параллельная загрузка с использованием async/await

Этот подход оптимален, когда источники независимы и могут загружаться одновременно.

suspend fun loadFromTwoSourcesParallel(
    source1: suspend () -> Result<Data>,
    source2: suspend () -> Result<Data>
): Pair<Result<Data>, Result<Data>> {
    val deferred1 = CoroutineScope(Dispatchers.IO).async { source1() }
    val deferred2 = CoroutineScope(Dispatchers.IO).async { source2() }
    
    return Pair(deferred1.await(), deferred2.await())
}

2. Последовательная загрузка с обработкой ошибок

Когда второй запрос зависит от результата первого или нужен контроль порядка:

suspend fun loadFromTwoSourcesSequential(): CombinedResult {
    return try {
        val firstResult = fetchFirstSource()
        val secondResult = fetchSecondSource(firstResult.data)
        CombinedResult.Success(firstResult, secondResult)
    } catch (e: Exception) {
        CombinedResult.Error(e)
    }
}

Расширенная реализация с Flow

Для реактивного подхода с возможностью обработки промежуточных состояний используйте Kotlin Flow:

fun loadFromTwoSourcesFlow(): Flow<LoadingState> = flow {
    emit(LoadingState.Loading)
    
    val results = listOf(
        async { fetchFirstSource() },
        async { fetchSecondSource() }
    ).awaitAll()
    
    when {
        results.all { it.isSuccess } -> {
            val data1 = results[0].getOrNull()
            val data2 = results[1].getOrNull()
            emit(LoadingState.Success(data1 to data2))
        }
        results.any { it.isFailure } -> {
            val error = results.firstOrNull { it.isFailure }?.exceptionOrNull()
            emit(LoadingState.Error(error ?: UnknownError()))
        }
    }
}.flowOn(Dispatchers.IO)

Продвинутые техники

Комбинирование с таймаутами и отменой

suspend fun loadWithTimeout(timeoutMs: Long = 5000) = 
    withTimeout(timeoutMs) {
        supervisorScope {
            val first = async { loadFirstSource() }
            val second = async { loadSecondSource() }
            
            try {
                val result1 = first.await()
                val result2 = second.await()
                mergeResults(result1, result2)
            } catch (e: TimeoutCancellationException) {
                throw SourceTimeoutException("Loading timeout exceeded")
            }
        }
    }

Ключевые аспекты реализации

  • Распределение по потокам: Используйте Dispatchers.IO для сетевых операций и Dispatchers.Main для обновления UI
  • Обработка ошибок: Реализуйте стратегию fallback или кэширование при сбое одного из источников
  • Отмена операций: Учитывайте жизненный цикл компонентов через CoroutineScope.launch с Job
  • Кэширование: Сохраняйте результаты для оптимизации повторных запросов
  • Тестирование: Используйте TestCoroutineDispatcher для модульного тестирования

Пример полной реализации

class DataLoader(
    private val localSource: LocalDataSource,
    private val remoteSource: RemoteDataSource
) {
    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
    
    fun loadCombinedData(): Flow<Resource<CombinedData>> = channelFlow {
        send(Resource.Loading)
        
        try {
            val localDeferred = scope.async(Dispatchers.IO) {
                localSource.getData()
            }
            
            val remoteDeferred = scope.async(Dispatchers.IO) {
                remoteSource.fetchData()
            }
            
            val localResult = localDeferred.await()
            val remoteResult = remoteDeferred.await()
            
            val combined = CombinedData(
                local = localResult,
                remote = remoteResult,
                timestamp = System.currentTimeMillis()
            )
            
            send(Resource.Success(combined))
        } catch (e: Exception) {
            send(Resource.Error(e, getCachedData()))
        }
    }
    
    fun cancel() {
        scope.cancel()
    }
}

Выбор конкретной реализации зависит от бизнес-требований: нужна ли параллельная загрузка, как обрабатывать частичные сбои, требуется ли поддержка промежуточных состояний. Наиболее гибким и современным подходом является использование Kotlin Flow с корутинами, что обеспечивает реактивность, отмену операций и чистую архитектуру.

Как реализовать метод который загружает асинхронную загрузку из 2 источников? | PrepBro