考虑这个用例。
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>
}
我从两个不同的数据源中获取数据,并将它们合并。现在,这是在连续运行。当我想运行 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
是不鼓励的,因为当调用者的生命周期结束时,该作业不会被取消。
由于我的usecase不知道调用者的生命周期,它应该使用哪个作用域?将作用域传递给usecase是一个可以接受的解决方案吗?
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())
}
或者这里最理想的解决方案是什么?usecase应该返回一个 Deferred
并让来电者 await
它?
不要传递 scope
对你的 UseCase
因为你将打破 Clean Architecture
. 而不是简单地保持你的函数为 Suspend
. 如果你希望函数是 concurrent
,只需将它们包在 async
块。
使用 UseCase
在你 ViewModel
并使用 ViewModelScope
呼叫 suspend
功能。
override suspend fun getPhotos(): List<Photo> {
val photos = async { photosRepository.getPhotos() }
val favoriteIds = async { favoritesRepository.getFavoriteIds() }
return photos.await().map {
it.copy(isFavorite = favoriteIds.await().contains(it.id))
}
}
如前所述 async
需要一个范围,一个好的解决方案是创建一个简单的 CoroutineScope
在 UseCase
其工作是对这个单一的coroutine进行作用域,以运行这两个 async
同时进行的任务。
val customScope = CoroutineScope(Dispatchers.Main)
override suspend fun getPhotos(): List<Photo> {
customScope.launch {
val photos = async { photosRepository.getPhotos() }
val favoriteIds = async { favoritesRepository.getFavoriteIds()}
}
.
.
.
}