Using coroutines to avoid callback hell when using XMLHttpRequest


#1

Hi!

Until now we have been using a bridge from Kotlin to the Bluebird promise API to avoid callback hell when using XMLHttpRequest.

Now that Kotlin 1.1 has arrived with coroutines,
what would be the idiomatic way to use coroutines to solve this problem?

I’ve been experiencing with some code from a previous post and the docs… but feel a bit unsure whether I’m on the right track.

Are Kotlin coroutines for Javascript ready enough to replace javascript promises? If so, would anyone like to give some guidance on how?

Thanks for any help!

Jørund

// Taken from: https://discuss.kotlinlang.org/t/async-await-on-the-client-javascript/2412/3
fun launch(block: suspend () -> Unit) {
    block.startCoroutine(object : Continuation<Unit> {
        override val context: CoroutineContext get() = EmptyCoroutineContext
        override fun resume(value: Unit) {}
        override fun resumeWithException(e: Throwable) {
            console.log("Coroutine failed: $e")
        }
    })
}

suspend fun send(request: XMLHttpRequest): String = suspendCoroutine { cont ->

    request.onreadystatechange = { event ->

        val readyState = request.readyState
        if (readyState == XMLHttpRequest.DONE) {

            if (request.status == 200.toShort()) {
                cont.resume(request.responseText)
            } else {
                cont.resumeWithException(RuntimeException("Unexpected response status ${request.status}"))
            }
        }
    }
    request.send()
}

class PersonDao {
   get() : Person // obviously this would not work with the code below, but what to wrap Person in?
      launch { // Where should launch be called? 
         val request = XMLHttpRequest()
         request.open("GET", "person.json")
          val personJson: Json = send(request).run {
              JSON.parse(this)
           }
           return Person.from(personJson)
        }}
    }
    }

Promise coroutines: await doesn't actualy wait
#2

No, but you can use Javascript Promise using async/await style to avoid callback-hell.

I’m writing a talk using Vert.x to solve same problem and my code is really similar to your.

Here is my simple solution:


#3

I’ve been experiencing with some code from a previous post and the docs… but feel a bit unsure whether I’m on the right track.

The code you provided looks fine. Did you try it? Are there any problems?

Are Kotlin coroutines for Javascript ready enough to replace javascript promises?

No, they are even not meant to. Kotlin coroutines are not replacement for promises, but enhancement. You can write a similar coroutine for promises, so you’ll be able to easily create and use promises.

Here’s an example:

public fun <T> async(c: suspend () -> T): Promise<T> {
    return Promise { resolve, reject ->
        c.startCoroutine(object : Continuation<T> {
            override fun resume(value: T) = resolve(value)

            override fun resumeWithException(exception: Throwable) = reject(exception)

            override val context = EmptyCoroutineContext
        })
    }
}

public suspend fun <T> Promise<T>.await() = suspendCoroutine<T> { c ->
    then({ c.resume(it) }, { c.resumeWithException(it) })
}

#4

Hi again!

Thanks for input from both @fvasco and @Alexey.Andreev. Sorry for late follow up.

@Alexey.Andreev you wrote [quote=“Alexey.Andreev, post:3, topic:2450”]
Kotlin coroutines are not replacement for promises, but enhancement.
[/quote]
Not sure if I follow you correctly. Does that mean that I still need to depend on an external promise api (map the class Promise with external) or will your example be fully functional without?

Another question. I’m writing a web component library, where each component (class) would override a method named render where it would append any child elements or apply and css styles. Also possibly load some data asynchronously and present it after completion. Since calling a suspending function either requires to be called from another suspending function or a coroutine I end up declaring the function render on the base component class as a suspending function. This function render would be invoked for every element laid out on the web page. Most render functions would never do any remote calls and are not in need for the suspending functionality. In addition some components, like the table component are dependent on speed when rendering it’s rows and columns. Should I reconsider this idea from a performance point of view? Would the suspending modifier cause any overhead on the functions when there is actually no asynchronous calls made within them?

Thanks!
Jørund


#5

After experiencing a bit I figured out that making the method render a suspending function was a bad idea.

First it had a huge impact on my builder DSL code, where suspend became the most frequently written word :slight_smile:

Secondly, since it’s not a preferred way of programming to let the view gather it’s data remotely anyway, I figured out it was better to “launch to coroutine” inside the render method instead.


#6

Not sure if I follow you correctly. Does that mean that I still need to depend on an external promise api (map the class Promise with external) or will your example be fully functional without?

You don’t have to depend on anything. But you should note that there are existing APIs and libraries that provide some external asynchronous API, and Kotlin coroutines allow to interact with these APIs. It’s a matter of your choice. You can depend on Promise, or you can create your own promises. You can wrap all non-promise APIs into promises, or you can create specialized await() functions. Kotlin just provides language feature, and you can choose how to use it.


#7

Is there any way on the JavaScript backend to get the Kotlin compiler, or IntelliJ, to warn you if you’re not at some point using a Promise that’s returned from somewhere? That’s the mistake I made several times when first using Promises.

This isn’t so important for functions where you’re actually going to use the value, because you’ll get a type-check error since you have Promise<T> where you need T. But it could be a problem for functions which just have side-effects, e.g., in my case, PouchDB’s db.destroy method, which just returns {"ok": true} or throws.

On the Java backend we could use javax.annotations.CheckReturnValue but I don’t know of anything similar in JS.