Getting Started With Kotlin-React — Part III
In the second part we have integrated some libraries like Axios and Lodash into the Giphy Search App. We have used @JsModule
and generated Kotlin types to access the libraries in a more Kotlin-like, typed way. In this last part, we will use Kotlin coroutines to deal with asynchronous calls. You will find the complete source on GitHub.
Dealing with Asynchronicity
The giphySearch()
function in GiphyApi.kt
performs the search using Axios, which makes an asynchronous HTTP call. The async get()
function returns a Promise, which we use to process the response by passing a (callback-) function to then()
. The return value of the giphySearch()
function is itself a Promise providing either the search result or a failure:
The giphySearch()
function in turn is used by the App to start a search when a user types into the search input field. Again, we pass functions tothen()
and catch()
of the Promise returned by giphySearch()
, which actually handle the search result resp. any errors:
Promises are quite an improvement compared to the callback hell we had to live with, so what´s wrong with that? When it comes to deal with conditionals, people still tend to write code pyramids like with callbacks. Anyway, cascading promises still kinda obfuscate the program flow.
In Typescript and ES8 you can use async-await which is basically just some syntactic sugar on top of promises. But it allows to (a)wait for an asynchronous call making your code look synchronous. Let’s compare some code handling promises with then()
…
…with a variant using async
-await
:
Asynchronous functions are prefixed with the async
keyword; await
suspends the execution until an asynchronous function return promise is fulfilled and unwraps the value from the Promise
returned. The value returned by an async function is automatically wrapped into a promise.
Using Kotlin Coroutines
Kotlin provides Coroutines to deal asynchronicity. Coroutines are computations that can be suspended without blocking a thread. Read more about this in the coroutines guide. In order to use coroutines in our Kotlin-React sample you need to install kotlinx-coroutines-core
:
After that you should run the gen-idea-libs
script which integrates the lib into the IntelliJ project, so you can use it in the IDE:
Older versions of the script had some issues, so if you run into any failure, just add the library manually in IntelliJ in the project structure. Just add a Kotlin/JS library and set the path to the kotlinx-coroutines-core
folder in the node-modules:
Now we are ready to write a variant of the giphySearch()
function based on coroutines. Since we want our function to be suspendable it starts with the keyword suspend
:
We simply call Axios.get()
, and in the next line we can access the result just as it was sequential, synchronous code. But where does the suspension actually happen? Notice the await()
function we are calling on the promise returned by Axios. This function is provided by the coroutines library and implements a suspension point that resumes when the promise is resolved:
So there is no need to deal with then()
and watch the return type of our search function: it is no longer a promise like in giphySearch()
…
…but directly the array of giphies:
So our search itself is now suspendable; now we have to alter the code in App.kt
that actually triggers the search. Let’s recap our promise based version which calls the gipyhSearch()
and sets the result state in in the then()
of the returned promise, resp. the error state in case of an exception:
Now we are gonna write a coroutines based version that calls giphySearchCoroutines()
in our App
. Suspendable functions itself can only be called by suspendable functions. So we could make this new function suspendable, and the function that calls it, and so on… But you gotta start somewhere to bridge the blocking and non-blocking worlds. There are several builder functions that helps you starting a new coroutine, e.g. launch()
and async()
. In our example we will use async()
to start a new coroutine:
Watch the call to giphySearchCoroutines()
: we directly get the result. There is no need for await or anything like that, since we are now calling a suspendable function from within a coroutine scope. We can even simply surround it with try-catch
to handle errors. Once inside a coroutine you don’t have to worry about asynchronicity when calling suspendable functions. Alls these nifty little details of suspend and resume are hidden, you are using it like it was synchronous code. And best of all: whether you are writing Kotlin/JS or Kotlin/JVM code, you always apply the same metaphors to deal with asynchronicity by using coroutines.
This is the end of our tiny tour through Kotlin-React. We have created functional, stateless and stateful components, integrated JS libraries, and finally dealt with asynchronicity. Hope you enjoyed the ride.
Ralf
Wherever possible I try to communicate asynchronously.
Elon Musk