修改延期结果

Nik*_*nko 8 coroutine kotlin rx-java2 kotlin-coroutines

给定一个返回模型的API(由Retrofit实现).我将一个老式的Call包含在一个Deferred使用extention函数中:

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
}
Run Code Online (Sandbox Code Playgroud)

现在我可以得到这样的模型:

data class Dummy(val name: String, val age: Int)

fun getDummy(): Deferred<Dummy> = api.getDummy().toDeferred()
Run Code Online (Sandbox Code Playgroud)

但是如何修改里面的对象Deferred并返回一个Deferred:

fun getDummyAge(): Deferred<Int> {
    // return getDummy().age
}
Run Code Online (Sandbox Code Playgroud)

我是协同程序的新手,所以可能这不是这里的事情.假设我是RxJava粉丝,我会实现这样的情况:

fun getDummy(): Single<Dummy> = api.getDummy().toSingle()

fun getDummyAge(): Single<Int> = getDummy().map { it.age }
Run Code Online (Sandbox Code Playgroud)

那么我应该尝试DeferredgetDummyAge函数返回一个?或者可能最好suspended fun尽可能地声明一个并调用deferred.await()我所有api的方法?

Rom*_*rov 22

如果您遵循异步编程风格,即编写返回的函数Deferred<T>,那么您可以像这样定义异步函数 getDummyAge:

fun getDummyAge(): Deferred<Int> = async { getDummy().await().age }
Run Code Online (Sandbox Code Playgroud)

但是,这种编程风格一般不建议在Kotlin中使用.惯用的Kotlin方法是使用以下签名定义暂停扩展函数 Call<T>.await():

suspend fun <T> Call<T>.await(): T = ... // more on it later
Run Code Online (Sandbox Code Playgroud)

并使用它来编写直接返回类型结果的挂起函数 getDummy,而不将其包装为延迟:Dummy

suspend fun getDummy(): Dummy = api.getDummy().await()
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您可以轻松编写挂起函数 getDummyAge:

suspend fun getDummyAge(): Int = getDummy().age
Run Code Online (Sandbox Code Playgroud)

对于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))
            }
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

如果你想进一步了解异步和暂停功能之间的风格差异,那么我建议你观看KotlinConf 2017中的Coroutines简介.如果你喜欢简短阅读,那么设计文档中的这一部分也提供了一些见解. .

  • @NikolayKulachenko您还可以使用 https://github.com/gildor/kotlin-coroutines-retrofit ,它已经具有此扩展(以及 https://github.com/JakeWharton/retrofit2-kotlin-coroutines-adapter 来走另一条路并直接在 Retrofit API 中使用“Deferred”)。 (2认同)