什么时候在 kotlin 中一起使用挂起函数和 Flow 或分开使用?

问题描述 投票:0回答:2

在审查一些用 kotlin 编写的代码时,有件事引起了我的注意。我在一些项目中查看领域层,在一些项目中,我看到暂停功能和 Flow 一起使用,在一些项目中,我看到只使用 Flow。

例如一起暂停和流动

class FetchMovieDetailFlowUseCase @Inject constructor(
    private val repository: MovieRepository
) : FlowUseCase<FetchMovieDetailFlowUseCase.Params, State<MovieDetailUiModel>>() {

    data class Params(val id: Long)

    override suspend fun execute(params: Params): Flow<State<MovieDetailUiModel>> =
        repository.fetchMovieDetailFlow(params.id)
}

只是流动

class GetCoinUseCase @Inject constructor(
    private val repository: CoinRepository
){
 
    operator fun invoke(coinId:String): Flow<Resource<CoinDetail>> = flow {

        try {
            emit(Resource.Loading())
            emit(Resource.Success(coin))

        }catch (e:HttpException){
            emit(Resource.Error(e.localizedMessage ?: "An unexpected error occured"))
        }catch (e:IOException){
            emit(Resource.Error("Couldn't reach server. Check your internet connection."))
        }
    }
}

暂停

class GetLatestNewsWithAuthorsUseCase(
  private val newsRepository: NewsRepository,
  private val authorsRepository: AuthorsRepository,
  private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default
) {
    suspend operator fun invoke(): List<ArticleWithAuthor> =
        withContext(defaultDispatcher) {
            val news = newsRepository.fetchLatestNews()
            val result: MutableList<ArticleWithAuthor> = mutableListOf()
            // This is not parallelized, the use case is linearly slow.
            for (article in news) {
                // The repository exposes suspend functions
                val author = authorsRepository.getAuthor(article.authorId)
                result.add(ArticleWithAuthor(article, author))
            }
            result
        }
}

这三个都是不同的项目,不要纠结于代码,这些只是我遇到的项目,我分享是为了展示例子,但我想在这里引起你注意的是,有时只有使用suspend函数,有时只使用Flow,有时两者都用。这是什么原因?你能详细解释一下吗? 我正在努力将其纳入我的逻辑

kotlin suspend flow
2个回答
0
投票

之所以有的项目只用suspend功能,有的只用Flow,有的两者都用,主要是因为项目的用例和需求不同

suspend 函数用于返回单个结果的异步操作。它们用于暂停协程的执行,直到操作完成,然后返回结果。此类操作的示例包括网络调用、磁盘 I/O 或数据库查询。

另一方面,Flow 用于随时间返回多个值的异步操作。它是一个可以发出多个值的反应流,并且可以随时取消流。此类操作的示例包括收听数据库或网络提要的实时更新。

在某些情况下,只需要一个结果,那么使用挂起函数就足够了。例如,从数据库或网络调用中获取单个项目时。在这种情况下,使用 Flow 就有点矫枉过正了。

在其他情况下,期望有多个结果,或者结果随时间更新时,Flow更合适。例如,在获取数据库或网络提要的实时更新时。在这种情况下,使用 suspend 是不够的,因为它只返回一个结果。

在某些情况下,suspendFlow可以一起使用。例如,在获取数据库的实时更新时,您可能会使用挂起函数来获取初始数据,然后使用 Flow 来接收实时更新。另一个例子是当你需要在一个序列中执行多个异步操作时,你可以使用挂起函数将它们链接在一起,然后使用 Flow 发出结果。

总而言之,选择使用挂起函数、Flow,还是两者兼而有之,取决于具体的用例和项目的要求。

使用suspend的例子:

suspend fun getWeatherData(city: String): WeatherData {
val response = weatherApi.getWeatherData(city)
return response.body() ?: throw Exception("Failed to fetch weather data")
}

在此示例中,

getWeatherData
函数使用 suspend 进行网络调用以获取给定城市的天气数据。该函数在返回数据之前等待响应返回。

使用Flow的例子:

fun getNewsArticles(): Flow<List<Article>> = flow {
val response = newsApi.getArticles()
val articles = response.articles
emit(articles)
}

在此示例中,

getNewsArticles
函数返回新闻文章的Flow。该函数使用 flow 创建可以异步收集的数据流。该函数进行网络调用以获取新闻文章并在数据可用时立即发出数据。然后,调用者可以收集随时间发出的数据。


0
投票

suspend
函数对于以暂停、同步的方式检索单个项目是有意义的。

当您决定开始收集时,流程会随着时间的推移返回多个项目。它们通常用于监视随时间变化的事物,以检索事物的最新值。例如,每当数据库中的某些内容发生变化时,来自数据库的流可以发出一个新值。

使用检索 Flow 的挂起函数将两者结合起来几乎没有意义。这是因为 Flow 本身是一个轻量级对象,通常不需要任何前期、耗时的工作来检索和创建。

在返回流时避免制作函数

suspend
的原因是这样使用起来比较麻烦。它会阻止你在协程之外使用这个漂亮的模式:

someUseCase.retrieveSomeFlow()
    .onEach { ... }
    .launchIn(someScope)

如果

retrieveSomeFlow()
是一个挂起函数,你必须这样做:

someScope.launch {
    someUseCase.retrieveSomeFlow().collect {
        ...
    }
}

在某些情况下,您可能只想传递流而不收集它,并且您不想为了获取流而必须启动协程。

如果您必须检索某些东西以用作作为流基础的键,则可能是一个例外。例如,从 API 检索用户 ID,然后返回使用该用户 ID 为流获取项目的流。但即使这样通常也是不必要的,因为您可以将该 ID 检索放入流程中。例如:

fun getUserItems(): Flow<UserItem> = flow {
    val userId = someAPI.retrieveUserId() // calling a suspend function
    emitAll(someAPI.getUserItemsFlow(userId))
}

唯一的缺点是,如果您多次收集流,则每次收集开始时都必须重新检索用户 ID。

© www.soinside.com 2019 - 2024. All rights reserved.