在 Kotlin 中使异步调用同步

aus*_*ser 6 java kotlin

我有一个我无法控制的 API.... 它包含一个方法,该方法执行一些工作并异步返回结果。我想在我的应用程序的某些部分同步调用这个方法。我通过添加一个ResultHandler捕获并返回结果的类来做到这一点。有没有比我在下面做的方法更好的方法?也许使用标准的 kotlin(或 Java 作为最后的手段)库方法。我的偏好是 awaitReply返回结果并删除CountdownLatch.

class Main {
    companion object {
        @JvmStatic

        fun main(args: Array<String>) {
            val result1 = Main().nonAsyncMethod1(arrayListOf(1, 2, 3, 4, 5))
            result1.elements.forEach { println(it) }
        }
    }

    class Result1(var elements: Collection<String>)

    fun asyncMethod1(x: Collection<Int>, callback: (Result1) -> Unit) {
        Thread().run {
            // do some calculation
            Thread.sleep(1000)
            callback(Result1(x.map { "\"$it\"" }.toList()))
        }
    }

    private fun nonAsyncMethod1(entities: Collection<Int>): Result1 {
        val resultHandler = ResultHandler<Result1>()

        awaitReply<Result1> {
            asyncMethod1(entities, resultHandler)
        }
        return resultHandler.getResponse()
    }

    open class ResultHandler<T : Any> : (T) -> Unit {
        private lateinit var response: T
        private val latch = CountDownLatch(1)

        override fun invoke(response: T) {
            latch.countDown()
            this.response = response
        }

        fun getResponse(): T {
            latch.await()
            return response
        }
    }

    private fun <T : Any> awaitReply(call: () -> Unit) {
        return call.invoke()
    }
}
Run Code Online (Sandbox Code Playgroud)

aus*_*ser 5

感谢 the_dani 的提示

我设法使用协程找到了下面的解决方案,详细信息请参阅Kotlin 协程文档的“包装回调”部分:

class Main {
    companion object {
        @JvmStatic

        fun main(args: Array<String>) = runBlocking {
            val result1 = Main().nonAsyncMethod1(arrayListOf(1, 2, 3, 4, 5))
            result1.elements.forEach { println(it) }
        }
    }

    class Result1(var elements: Collection<String>)

    fun asyncMethod1(x: Collection<Int>, callback: (Result1) -> Unit) {
        Thread().run {
            // do some calculation
            Thread.sleep(1000)
            callback(Result1(x.map { "\"$it\"" }.toList()))
        }
    }

    suspend fun nonAsyncMethod1(entities: Collection<Int>): Result1 = suspendCoroutine {
        cont ->
            asyncMethod1(entities) { cont.resume(it) }
    }
}
Run Code Online (Sandbox Code Playgroud)


Cit*_*sO2 5

您可以使用协程将异步函数包装为回调(协程类似于 C# async/await,您可以创建看起来非常同步的异步代码,但实际上是异步执行的)

https://github.com/Kotlin/KEEP/blob/master/proposals/coroutines.md#wrapping-callbacks

有一个简单的模式。假设您有一些具有回调的 LongComputation 函数,该函数接收作为此计算结果的一些值。

fun someLongComputation(params: Params, callback: (Value) -> Unit)` 
Run Code Online (Sandbox Code Playgroud)

您可以使用以下简单代码将其转换为挂起函数:

suspend fun someLongComputation(params: Params): Value = suspendCoroutine { cont ->
    someLongComputation(params) { cont.resume(it) } 
}
Run Code Online (Sandbox Code Playgroud)

挂起函数只能在协程上下文中调用(例如使用launch{ }),但为了等待,您可以使用runBlocking{ }which 来等待协程完成。这应该会创建您想要的行为。