等待几个流程完成后再继续

时间:2020-04-15 15:37:49

标签: android kotlin-coroutines kotlin-coroutines-flow

我需要将图像上传到服务器,并且这样做我不能使用其他库,而是将其(以base64编码)拆分为块并全部上传。

我正在使用Kotlin协程Flows,而我目前正在做的是第一个调用(返回流)以获得需要在所有上传请求中附加的图片ID

这是我用来上传图片的2个功能

fun submitImage(payload: Payload): Flow<String> {
    val request = requestBuilder.buildUploadImageRequest(payload)
    return client.execute(request)
        .serviceFlow({ response ->
            val imageId = response.body.id
            uploadImage(payload.imageBase64, imageId)
            imageId
        }, { response ->
            throw MyServerError("Error ${response.error}")
        })
}

private fun uploadImage(imageBase64: String, imageId: String) {
    val chunks = divideEncodedImageInChunksOfSize(imageBase64)
    var v = 1
    for (chunk in chunks) {
        val payload = generatePayload(imageId, v, chunk, false)
        submitImageChunk(payload)
        v++
    }
    val payload = generatePayload(imageId, v, "", true)
    submitImageChunk(payload)
}

private fun submitImageChunk(payload: JSONObject): Flow<Unit> {
    val request = requestBuilder.buildUploadImageChunkRequest(payload)
    return client.execute(request)
        .serviceFlow({ }, { response ->
            throw MyHttpError(response)
        })
}

我利用以下实用程序功能

// Extension function to handle Flows and their activation
internal fun MyHttpClient.execute(request: MyHttpRequest): Flow<MyHttpResponse> {
    return flow {
        val deferred = CompletableDeferred<MyHttpResponse>()
        executeHttp(request, object : MyHttpListener {
            override fun onSuccess(response: MyHttpResponse) {
                deferred.complete(response)
            }

            override fun onFailure(response: MyHttpResponse) {
                deferred.completeExceptionally(MyHttpError(response))
            }
        })
        emit(deferred.await())
    }
}

// Extension function to catch exceptions AND to check if the response body is null
internal fun <T> Flow<MyHttpResponse>.serviceFlow(
    onSuccess: (response: MyHttpResponse) -> T,
    onError: (response: MyHttpResponse) -> Unit
) = flatMapConcat { response ->
    flowOf(response)
        .map { res ->
            res.body?.let { it ->
                onSuccess(res)
            } ?: throw MyParseError("MyHttpResponse has a null body")
        }
        .catchException<JSONException, T> { e ->
            throw MyParseError("Parsing exception $e")
        }
}.catchException<MyHttpError, T> { e ->
    onError(e.response)
}

// Function leveraging OkHttpClient to make a HTTPRequest
internal fun executeHttp { ... }

我认为问题是由于以下事实:函数submitImage在启动所有子流以上传图像后返回,但它并不等待所有子流完成。 我不确定Kotlin协程对于这样的用例有什么构造,有人可以帮助我吗?

2 个答案:

答案 0 :(得分:0)

我认为您应该使用WorkManager并考虑使用chain woker feature

具有Flow功能,请尝试以下操作:

private suspend fun uploadImage(imageBase64: String, imageId: String) {
withContext(Dispatchers.IO){
  val chunks = divideEncodedImageInChunksOfSize(imageBase64)
  var v = 1
  for (chunk in chunks) {
     val payload = generatePayload(imageId, v, chunk, false)
     submitImageChunk(payload)
     v++
   }
  val payload = generatePayload(imageId, v, "", true)
  submitImageChunk(payload).await();
 }

private suspend fun submitImageChunk(payload: JSONObject): Deferred<Unit> {
 val request = requestBuilder.buildUploadImageChunkRequest(payload)
 return client.execute(request);
}

答案 1 :(得分:0)

谢谢musafee使我朝着正确的方向前进。

最后的答案是,我在router.post("/new-opportunity", passport.authenticate("jwt", { session: false }), (req, res) => { let user = req.user; let newPost = req.body; let companyId = user.company_id; const boardPost = { name: newPost.name, }; Company.updateOne( { _id: companyId }, { $push: { board_posts: boardPost, }, } ) .then((result) => { console.log(result); // result.n; // Number of documents matched // result.nModified; // Number of documents modified res.send(result); }) .catch((error) => { console.log(error); res.status(500); }); }); 函数中创建了这些流,但实际上我从未在它们上调用uploadImage,因此它们仍然没有启动。

我选择的解决方案是将在那里创建的流的列表返回给调用函数,并从那里将collect函数的返回类型从submitImage更改为{{1 }},并从上层触发它们