4

Is there a way to write the Kotlin code below so that it compiles and works the same way on the JVM and in JavaScript?

fun <A: Any> request(request: Any): A  = runBlocking {
    suspendCoroutine<A> { cont ->
        val subscriber = { response: A ->
                cont.resume(response)
        }
        sendAsync(request, subscriber)
    }
}


fun <Q : Any, A : Any> sendAsync(request: Q, handler: (A) -> Unit) {

    // request is sent to a remote service,
    // when the result is available it is passed to handler(... /* result */)

}

The code compiles and works fine when compiled to target the JVM. A compilation error is emitted when targeting JavaScript due to non-existent function runBlocking

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
auser
  • 6,307
  • 13
  • 41
  • 63

1 Answers1

3

Your main problem is that you aren't asking for the thing you actually need. The code you wrote starts a coroutine, suspends it, then blocks until it's done. This is exactly equivalent to having no coroutines at all and just making a blocking network request, which is something you can't possibly expect JavaScript to allow you.

What you actually have to do is step back to the call site of request() and wrap it in a launch:

GlobalScope.launch(Dispatchers.Default) {
    val result: A = request(...)
    // work with the result
}

With this in place you can rewrite your request function to

suspend fun <A: Any> request(request: Any): A = suspendCancellableCoroutine {
    sendAsync(request, it::resume)
}
Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
  • I don't think GlobalScope::launch is part of the Kotlin js coroutine library - this code won't compile if the target is JS – auser Sep 19 '18 at 14:52
  • 1
    Also, I'm trying to avoid adding "suspend" to the "request" method's signature because that mean the call site has to be wrapped in a coroutine – auser Sep 19 '18 at 15:29
  • `GlobalScope` is in `kotlinx.coroutines.experimental` so it must be available on any platform, however I don't know if it's the best option on JS. You can't avoid `suspend` and launching coroutines because that's the essence of how this works. – Marko Topolnik Sep 19 '18 at 16:23
  • Ah, one important note: my code is written against version `0.26` of `kotlinx-coroutines-experimental`, recently released. It deprecates several key mechanisms like the global `launch` without a receiver. – Marko Topolnik Sep 19 '18 at 16:31