Рассмотрим этот вариант использования:
class GetPhotosUseCase(
private val photosRepository: IPhotosRepository,
private val favoritesRepository: IFavoritesRepository
) : IGetPhotosUseCase {
override suspend fun getPhotos(): List<Photo> {
val photos = photosRepository.getPhotos()
val favoriteIds = favoritesRepository.getFavoriteIds()
return photos.map {
it.copy(isFavorite = favoriteIds.contains(it.id))
}
}
}
interface IPhotosRepository {
suspend fun getPhotos(): List<Photo>
}
interface IFavoritesRepository {
suspend fun getFavoriteIds(): List<Int>
}
Я выбираю данные из 2 разных источников и объединяю их. Прямо сейчас это работает последовательно. Когда я хочу запустить photosRepository.getPhotos()
и favoritesRepository.getFavoriteIds()
параллельно, чтобы сэкономить время выполнения, мой наивный подход будет следующим:
override suspend fun getPhotos(): List<Photo> {
val photosDeferred = GlobalScope.async { photosRepository.getPhotos() }
val favoriteIdsDeferred = GlobalScope.async { favoritesRepository.getFavoriteIds() }
return applyFavoritesToPhotos(photosDeferred.await(), favoriteIdsDeferred.await())
}
private fun applyFavoritesToPhotos(photos: List<Photo>, favoriteIds: List<Int>) = photos.map {
it.copy(isFavorite = favoriteIds.contains(it.id))
}
Использование GlobalScope
не рекомендуется, потому что задание не будет отменено, когда жизненный цикл звонящего заканчивается.
Поскольку мой сценарий использования не знает о жизненном цикле вызывающего абонента, какую область он должен использовать? Было бы приемлемым решением передать область применения в сценарий использования, например:
override suspend fun getPhotos(scope: CoroutineScope): List<Photo> {
val photosDeferred = scope.async { photosRepository.getPhotos() }
val favoriteIdsDeferred = scope.async { favoritesRepository.getFavoriteIds() }
return applyFavoritesToPhotos(photosDeferred.await(), favoriteIdsDeferred.await())
}
, или что было бы идеальным решением здесь? Должен ли случай использования вернуть Deferred
и позволить вызывающему await
it?