给定一个返回模型的API(由Retrofit实现)。我使用扩展功能将老式的Call包装为Deferred:

fun <T> Call<T>.toDeferred(): Deferred<T> {
    val deferred = CompletableDeferred<T>()

    // cancel request as well
    deferred.invokeOnCompletion {
        if (deferred.isCancelled) {
            cancel()
        }
    }

    enqueue(object : Callback<T> {
        override fun onFailure(call: Call<T>?, t: Throwable) {
            deferred.completeExceptionally(t)
        }

        override fun onResponse(call: Call<T>?, response: Response<T>) {
            if (response.isSuccessful) {
                deferred.complete(response.body()!!)
            } else {
                deferred.completeExceptionally(HttpException(response))
            }
        }
    })

    return deferred
}

现在,我可以得到这样的模型:
data class Dummy(val name: String, val age: Int)

fun getDummy(): Deferred<Dummy> = api.getDummy().toDeferred()

但是如何修改Deferred内部的对象并返回Deferred:
fun getDummyAge(): Deferred<Int> {
    // return getDummy().age
}

我是协程新手,所以这可能不是这里的工作方式。假设我是RxJava粉丝,我将实现这种情况,例如:
fun getDummy(): Single<Dummy> = api.getDummy().toSingle()

fun getDummyAge(): Single<Int> = getDummy().map { it.age }

因此,我应该尝试从Deferred函数返回getDummyAge吗?还是最好在可能的情况下声明一个suspended fun并在我所有api的方法上调用deferred.await()

最佳答案

如果遵循异步编程风格,即编写返回Deferred<T>的函数,则可以定义异步函数getDummyAge,如下所示:

fun getDummyAge(): Deferred<Int> = async { getDummy().await().age }

但是,在Kotlin中通常不建议使用这种编程风格。惯用的Kotlin方法是使用以下签名定义一个悬挂的扩展功能Call<T>.await():
suspend fun <T> Call<T>.await(): T = ... // more on it later

并使用它编写暂停函数getDummy,该函数直接返回Dummy类型的结果,而无需将其包装到deferred中:
suspend fun getDummy(): Dummy = api.getDummy().await()

在这种情况下,您可以简单地编写挂起函数getDummyAge:
suspend fun getDummyAge(): Int = getDummy().age

对于翻新调用,您可以像这样实现await扩展:
suspend fun <T> Call<T>.await(): T = suspendCancellableCoroutine { cont ->
    cont.invokeOnCompletion { cancel() }
    enqueue(object : Callback<T> {
        override fun onFailure(call: Call<T>?, t: Throwable) {
            cont.resumeWithException(t)
        }

        override fun onResponse(call: Call<T>?, response: Response<T>) {
            if (response.isSuccessful) {
                cont.resume(response.body()!!)
            } else {
                cont.resumeWithException(HttpException(response))
            }
        }
    })
}

如果您想更多地了解异步函数和挂起函数之间的样式差异,那么我建议您观看KotlinConf 2017的Introduction to Coroutines

关于kotlin - 修改延期结果,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49762153/

10-14 22:50