代码之家  ›  专栏  ›  技术社区  ›  Florian Walther

emitAll,直到另一个操作完成?

  •  0
  • Florian Walther  · 技术社区  · 3 年前

    我在我的Android应用程序中使用以下代码将数据从远程源加载到本地数据库缓存中。

    inline fun <ResultType, RequestType> networkBoundResource(
        crossinline query: () -> Flow<ResultType>,
        crossinline fetch: suspend () -> RequestType,
        crossinline saveFetchResult: suspend (RequestType) -> Unit,
        crossinline onFetchSuccess: () -> Unit = { },
        crossinline onFetchFailed: (Throwable) -> Unit = { },
        crossinline shouldFetch: (ResultType) -> Boolean = { true }
    ) = flow {
        val data = query().first()
    
        val flow = if (shouldFetch(data)) {
            emit(Resource.(data))
    
            try {
                // this could take a while, I want to keep getting updates meanwhile
                saveFetchResult(fetch())
                onFetchSuccess()
                query().map { Resource.Success(it) }
            } catch (t: Throwable) {
                onFetchFailed(t)
                query().map { Resource.Error(t, it) }
            }
        } else {
            query().map { Resource.Success(it) }
        }
        emitAll(flow)
    }
    
    

    这个 query 是一个数据库查询,它通过以下方式不断发出数据库更新 emitAll 直到我们再次调用此方法。

    此设置的问题在于 Resource.Loading 仅包含当前数据的“快照”( first() )在结束之前,我们不会收到任何数据库更新 try/catch 阻止并呼叫 emitAll 。但我希望在以下期间继续接收数据库更新 Loading 仍在进行中。然而,我不能只是打电话 emitAll 资源。加载 因为它会阻塞整个流程。

    有办法打电话吗 emitAll 加载 然后切换到 Success / Error 一旦 try 区块已经完成?

    0 回复  |  直到 3 年前
        1
  •  3
  •   N1hk    3 年前

    我只对此进行了简单的测试来验证它,但看起来你可以监听查询,并根据外部Flow的上下文在新启动的协程中发出它传播的任何/所有数据——函数中的其他工作将继续进行,不受阻碍。一旦完成了其他工作,就可以取消监听查询的协程。例如:

    inline fun <ResultType, RequestType> networkBoundResource(
        crossinline query: () -> Flow<ResultType>,
        crossinline fetch: suspend () -> RequestType,
        crossinline saveFetchResult: suspend (RequestType) -> Unit,
        crossinline onFetchFailed: (Throwable) -> Unit = { },
        crossinline shouldFetch: (ResultType) -> Boolean = { true }
    ): Flow<Resource<ResultType>> = flow {
        emit(Resource.Loading())
        val data = query().first()
    
        val flow = if (shouldFetch(data)) {
            val flowContext = currentCoroutineContext()
            val loading: Job = coroutineScope {
                launch(flowContext) {
                    query().map { Resource.Loading(it) }
                        .collect { withContext(flowContext) { emit(it) } }
                }
            }
    
            try {
                val request = fetch()
                loading.cancel()
                saveFetchResult(request)
                query().map { Resource.Success(it) }
            } catch (throwable: Throwable) {
                loading.cancel()
                onFetchFailed(throwable)
                query().map { Resource.Error(throwable, it) }
            }
        } else {
            query().map { Resource.Success(it) }
        }
    
        emitAll(flow)
    }
    

    如果成功了,请告诉我!