在Kotlin中使用异步操作创建API的惯用方式是什么?

时间:2019-06-09 21:32:37

标签: kotlin kotlin-coroutines

在Kotlin中创建API时,处理异步操作的惯用方式是什么?

我们可以创建需要常规阻塞调用的api,从而迫使应用程序代码使用类似runBlocking之类的东西。

////
//// Plain functions 
////

// library code
fun registerHandler(block: (it: Foo) -> String) {
    // save a reference to call when an action happens later
}

// application code
registerHandler {
    runBlocking {
        handleItSuspend(it)
    }
}

我们可以更多地使用suspend,这对应用程序代码来说看起来更好,但是要求我们从暂停函数或协程上下文执行回调,这可能给我们带来麻烦,也可能不会带来麻烦。

////
//// Suspend functions
////

// library code
fun registerHandler(block: suspend (it: Foo) -> String) {
    // save a reference to call when an action happens later
}

// application code
registerHandler {
    handleItSuspend(it)
}

或者我们可以采用返回延迟结果的函数

////
//// Deferred functions
////

// library code
// This handler can be called from anywhere without needing suspend.
fun registerHandler(block: (it: Foo) -> Deferred<String>) {
    // save a reference to call when an action happens later
}

// application code
registerHandler {
    // Function that isn't suspend but returns a deferred
    handleItAsync(it)
}

对于我们应该做的事情是否有共识或正式立场?

1 个答案:

答案 0 :(得分:0)

正如@ msrd0在注释中提到的那样,您可以轻松地在第二种方法和第三种方法的参数之间进行转换,因此它们是等效的:

val block1: suspend (Foo) -> String = ...
val block2: (Foo) -> Deferred<String> = { x -> async { block1(x) } }
val block3: suspend (Foo) -> String = { x -> block2(x).await() }

但是我希望大多数对Deferred版本的调用看起来像

registerHandler { foo -> async { doSomethingSuspend(foo) } }

在这种情况下,suspend版本更易于使用,它将只是

registerHandler { foo -> doSomethingSuspend(foo) }

当然,您的代码库可能已经足够普遍使用Deferred了,以致这种假设不成立。

  

要求我们从挂起函数或协程上下文执行回调,这可能会给我们带来麻烦,也可能不会给我们带来麻烦。

我认为这不太方便,因为它只是async内部的一个简单的registerHandler(或其他协程生成器)调用。

或者您的意思是说registerHandler需要在那里打电话吗?仅当它被本身声明为suspend时,这种情况才会成立。