【问题标题】:Modify the Deferred result修改延迟结果
【发布时间】:2018-09-20 13:49:48
【问题描述】:

给定一个返回模型的 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 }

那么我应该尝试从getDummyAge 函数返回一个Deferred 吗?还是尽可能声明suspended fun 并在我所有的api 方法上调用deferred.await() 更好?

【问题讨论】:

    标签: kotlin rx-java2 coroutine kotlin-coroutines


    【解决方案1】:

    如果你遵循异步风格的编程,即编写返回Deferred&lt;T&gt;的函数,那么你可以像这样定义异步函数getDummyAge

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

    但是,这种编程风格在 Kotlin 中一般不推荐。惯用的 Kotlin 方法是使用以下签名定义 暂停扩展函数 Call&lt;T&gt;.await()

    suspend fun <T> Call<T>.await(): T = ... // more on it later
    

    并使用它来编写 暂停函数 getDummy 直接返回Dummy 类型的结果 而不将其包装到延迟中:

    suspend fun getDummy(): Dummy = api.getDummy().await()
    

    在这种情况下,您可以简单地编写 挂起函数 getDummyAge:

    suspend fun getDummyAge(): Int = getDummy().age
    

    对于 Retrofit 调用,您可以像这样实现 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。如果你喜欢简短的阅读,那么this section from the design document 也可以提供一些见解.

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-07-08
    • 1970-01-01
    • 2016-12-26
    • 1970-01-01
    • 2016-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多