Kotlin Coroutine Flow如何返回首个推荐电影同时继续执行后续更新操作
实现方案
完全可以实现你要的效果,核心思路是通过协程并发发起所有后端校验请求,匹配到第一个符合要求的电影后立刻返回,同时启动独立的子协程完成剩余校验和本地数据库更新操作,全程不阻塞返回流程。
方案1:保持原有逻辑(按字母序返回首个推荐电影)
和你原有逻辑一致,优先返回字母排序最靠前的推荐电影,同时所有后端请求并行执行,比原有串行实现快数倍:
import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch suspend fun getFirstRecommendedMovie(genre: String): Movie = coroutineScope { // 取当前流派下按字母排序的电影列表快照 val movies = dao.getMovies(genre).first() // 并行发起所有电影的推荐状态校验请求 val deferredChecks = movies.map { movie -> async { val isRecommended = endpoint.isMovieRecommended(movie).single() movie to isRecommended } } val allCheckResults = mutableListOf<Pair<Movie, Boolean>>() var firstMatch: Movie? = null // 按原有字母顺序遍历结果,找到第一个推荐电影立刻记录 for (deferred in deferredChecks) { val (movie, isRecommended) = deferred.await() allCheckResults.add(movie to isRecommended) if (firstMatch == null && isRecommended) { firstMatch = movie } } // 启动后台协程完成本地数据库更新,不阻塞当前返回流程 launch { val allRecommendedMovies = allCheckResults .filter { it.second } .map { it.first } dao.updateRecommendedMovies(allRecommendedMovies).single() } return@coroutineScope firstMatch ?: throw NoSuchElementException("该流派下无推荐电影:$genre") }
方案2:最快返回任意推荐电影
如果你不需要严格保持字母序优先级,只要拿到任意一个推荐电影就立刻返回,可以用select实现最快响应:
import kotlinx.coroutines.async import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import kotlinx.coroutines.selects.select suspend fun getFirstRecommendedMovieFastest(genre: String): Movie = coroutineScope { val movies = dao.getMovies(genre).first() val deferredChecks = movies.map { movie -> async { val isRecommended = endpoint.isMovieRecommended(movie).single() if (isRecommended) movie else null } } // 等待第一个返回的推荐电影 val firstMatch = select<Movie?> { deferredChecks.forEach { deferred -> deferred.onAwait { it } } } ?: throw NoSuchElementException("该流派下无推荐电影:$genre") // 后台更新数据库 launch { val allRecommendedMovies = deferredChecks.awaitAll().filterNotNull() dao.updateRecommendedMovies(allRecommendedMovies).single() } return@coroutineScope firstMatch }
注意事项
- 请确保函数调用时使用的协程Scope生命周期足够覆盖后台更新操作(比如使用
viewModelScope,避免页面销毁时更新任务被意外取消) - 可以根据需求给
async和launch块添加异常捕获逻辑,避免单个接口请求失败导致整个流程崩溃 - 如果需要支持离线场景,可以在调用后端接口前增加本地缓存判断,已经校验过的电影不用重复请求接口
内容的提问来源于stack exchange,提问作者kioli
相关产品推荐
相关产品推荐

