Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make asynchronous call synchronous in Kotlin

Tags:

java

kotlin

I have an API I have no control over.... which contains a method that does some work and returns the results asynchronously. I would like to call this method synchronously in parts of my application. I have done this by adding a class ResultHandler which captures and returns the result. Is there a better way of doing this than the way I've done it below? Perhaps using standard kotlin (or Java as last resort) library methods. My preference would be for awaitReply to return the result and also to remove the 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()
    }
}
like image 892
auser Avatar asked Dec 13 '18 15:12

auser


People also ask

Is Kotlin synchronous or asynchronous?

Coroutines are Kotlin features that allow you to surround your blocking calls(Synchronous) codes in non-blocking(Asynchronous) constructs. A coroutine is like a function or a call which by default runs Asynchronously in parallel with others.

How do you make a synchronous call asynchronous?

The simplest way to execute a method asynchronously is to start executing the method by calling the delegate's BeginInvoke method, do some work on the main thread, and then call the delegate's EndInvoke method. EndInvoke might block the calling thread because it does not return until the asynchronous call completes.

How do you call asynchronous Kotlin?

Coroutines Kotlin's approach to working with asynchronous code is using coroutines, which is the idea of suspendable computations, i.e. the idea that a function can suspend its execution at some point and resume later on.

What is CoroutineScope in Kotlin?

A CoroutineScope defines a lifecycle, a lifetime, for Coroutines that are built and launched from it. A CoroutineScope lifecycle starts as soon as it is created and ends when it is canceled or when it associated Job or SupervisorJob finishes.


2 Answers

Thanks to the hint from the_dani

I managed come to the solution below using coroutines as detailed in "Wrapping callbacks" section of the Kotlin coroutines documentation:

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) }
    }
}
like image 50
auser Avatar answered Oct 24 '22 17:10

auser


You can wrap async functions with a callback with coroutines (Coroutines are similar to C# async/await, you can create asynchronous code that looks very much synchronous, but which is executed asynchronous)

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

There is a simple pattern. Assume that you have someLongComputation function with callback that receives some Value that is a result of this computation.

fun someLongComputation(params: Params, callback: (Value) -> Unit)` 

You can convert it into a suspending function with the following straightforward code:

suspend fun someLongComputation(params: Params): Value = suspendCoroutine { cont ->
    someLongComputation(params) { cont.resume(it) } 
}

Suspend functions can only be called in a coroutine context (for example with launch{ }), but in order to wait you can use runBlocking{ } which should then wait for the coroutine to finish. This should create your desired behaviour.

like image 21
CitrusO2 Avatar answered Oct 24 '22 15:10

CitrusO2