在 Kotlin 中等待多个回调/lambda 的结果

5

我正在使用Kotlin制作一个应用程序。到目前为止,我的网络调用不需要一起使用。现在我需要进行两个并发的网络调用,等待直到我收到它们的响应,然后继续执行。我想要实现类似于这样的功能:

    //first networking call, get resourceOne
    var resourceOne : String?
    Server.asyncRequest(RequestBuilder(endpoints.second, ids, params)) { resource: String?, error: ServiceError? ->
        resourceOne = resource
    }

    //second networking call, get resourceTwo
    var resourceTwo : String?
    Server.asyncRequest(RequestBuilder(endpoints.third, ids, params)) { resource: String?, error: ServiceError? ->
        resourceTwo = resource
    }

    //do something here wiith resourceOne and resourceTwo

我的asyncRequest函数的函数头如下:

fun asyncRequest(requestBuilder: RequestBuilder, completion: (resource: String?, error: ServiceError?) -> Unit) {

它只是包装了一个 okhttp 请求并进行一些额外的处理/解析。通常我会在完成 lambda 内部获取结果(资源)并进行处理,但由于我需要两个值,所以我不能在这里这样做。我尝试过类似于这个链接的方法,但我的 asyncRequest 函数没有返回类型,因此我无法像链接中那样进行异步等待。

您可以使用 Kotlin 协程来实现此目的。 - Anggrayudi H
@Anggrayudi 我已经阅读了有关协程的文档,正如我在帖子中提到的那样,但是我不明白我应该如何在这种特定情况下应用它们。我找到的唯一示例是协程具有返回类型而不是回调函数。 - BillyDean
1个回答

7
您可以使用类似以下方式的协程来完成此操作:
使用suspendCancellableCoroutine {...}块将回调函数转换为可暂停函数
suspend fun <T> request(requestBuilder: RequestBuilder): T = suspendCancellableCoroutine { cont ->
    Server.asyncRequest(requestBuilder) { resource: T, error: ServiceError? ->
        if(error != null)
            cont.resumeWithException(error) // Makes the Flow throw an exception
        else
            cont.resume(resource) // Makes the Flow emit a correct result
    }
}

创建一个Flow以进行第一次请求:
val resourceOneFlow = flow {
    emit(request<String>(RequestBuilder(endpoints.second, ids, params)))
}

创建一个 流程 来执行 第二个请求:
val resourceTwoFlow = flow {
    emit(request<String>(RequestBuilder(endpoints.third, ids, params)))
}

使用 zip 运算符来合并两个数据流:

val requestsResultFlow = resourceOneFlow.zip(resourceTwoFlow) { resourceOne, resourceTwo ->
    // Build whatever you need with resourceOne and resourceTwo here and let it flow
    "$resourceOne $resourceTwo".length // Here I concatenate both strings and return its length
}

使用collect操作符激活/启动Flow,并消耗其结果

requestsResultFlow.collect { length ->
    // Consume the result here
    println("$length") // Here I print the number received
}

你可以在这里查看有关Flow的文档。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接