DEV Community

loading...

Quarkus - Reactive programming without callback headache

davide_d_cube profile image Davide Di Domenico ・4 min read

The world of asyncronous and reactive programming is a matter that the JavaScript developers know well. In the world of the "ancient" stacks is an approach that only on the last years is becoming a thing that seams to be look with a certain interest.
Especially in Java world there are years that there is support for multi threading computation, but this is an approach that is requested to parallelize long time consuming blocking situations, what the asyncronous and reactive programming try to resolve is to use better the resources that modern processors offer to the computational world.

Start from the basics: kotlin

Kotlin is a programming language that takes advantages, in terms of echosystem, from the JVM world infact it is a language that is part of the family of JVM languages.
Kotlin allows developers to develop their structures under different approaches such as functional programming, object oriented programming.
It is a modern language born with simplicity in mind, it is proposed as official programming language for Android applications development and allows to develop crossplatform code between iOS, Android, Web and Desktop.
This elasticity is not gifted by "some ghost behind the scenes" that works heavily to guarantee a limping compatibility with the supported platform, but simply the compiler behind the kotlin language knows how to translate and optimize the kotlin code into the different platforms to be compatible with.

A step forward: Quarkus

Quarkus is a mordern full stack framework that permits to develop backend applications with minimum effort and with maximum performance and specially allow Spring users to recycle theire knowledge to write applications that are scalable, require minimum memory footprint and are performant under the cloud world.
Quarkus can be used also as traditional framework for developing traditional monolitic backend services, but developer can do this with some advantages offered by the Event bus, Reactive programming, Many plugins ready to use and so on.

Asyncronous and Reactive programming

The scope of this article is not to dive into the techical differences and similarities between asyncronous and reactive programming, but what is matter to know is that the reactive programming is an approach to asyncronous programming.

Put all togheter

The reactive programming, as JavaScript developers know, requires that the result of an asyncronous action can be used into a function called callback that is passed as argument to the method that is computing the asyncronous result or, in other cases, the callback is hooked as a ring of a chain that forms a pipeline of computation.

This is a typical use of reactive programming applied to Quarkus framework.

public static Uni<Fruit> findById(PgPool client, Long id) {
    return client.preparedQuery("SELECT id, name FROM fruits WHERE id = $1").execute(Tuple.of(id)) 
            .onItem().transform(RowSet::iterator) 
            .onItem().transform(iterator -> iterator.hasNext() ? from(iterator.next()) : null); 
}
Enter fullscreen mode Exit fullscreen mode

Without entering in the specifications, what you can see is the fact that developer that needs to process a response of a certaing http call, needs to concatenate some methods for controlling the process of the information.
This is a behavior that the JavaScript and NodeJS worlds has resolved with the keywordss async and await, these keywords allow developer to describe the implementation without callbacks and reconduct all the implementation to the classic imperative code style delegating the compiler to resolve correctly the callbacks. Behind the scenes these two keywords are difficult to implement in a good way and this should have as final goal to use better the performance of the CPU.
In Java world the async and await is not implemented officialy, there is the project loom that in a near future will enable to write some code that look like this, but for now, if you want a clean and non blocking code, you need to write code with callbacks.

Kotlin, in the other hand, has the concept of coroutine that is very similar to the concept of async and await, but has more features and allow to control also the threads and process management. In this first approach lets say that we are happy if we can write performant code in a simple way.
Quarkus uses SmallRye Mutiny as library to perform all asyncronous operations, this library is very similar to the other libraries such as RXJava, RXJavascript, Reactor and so on, but has the advantage to be very simple and to have a limited family of operators that can be empowered by using in smart way the common Java functionalities.
With the version 0.13.0 Mutiny has accepted the Pull Request of a contrubutor that has implemented the coroutines as extended methods of Mutiny's main classes (Uni and Multi).
Now let's see the code reported before with kotlin language

fun findById(client: PgPool, id: Long?): Uni<Fruit?> {
    return client.preparedQuery("SELECT id, name FROM fruits WHERE id = $1").execute(Tuple.of(id))
        .onItem().transform(RowSet::iterator)
        .onItem().transform { iterator -> if (iterator.hasNext()) from(iterator.next()) else null }
}
Enter fullscreen mode Exit fullscreen mode

Now let's see the same code writen with assistance of the new library

suspend fun findById(client: PgPool, id: Long): Fruit? {
    val rowset = client.preparedQuery("SELECT id, name FROM fruits WHERE id = $1")
        .execute(Tuple.of(id))
        .awaitSuspending()
    val rowIterator = rowset.iterator()
    return if (rowIterator.hasNext()) {
        from(rowIterator.next())
    } else {
        null
    }
}
Enter fullscreen mode Exit fullscreen mode

As you can see the callbacks are disappeared and the basic things such as flow control or exception management is a little bit easy do manage.

Obviously is possible also to reconvert a coroutine to a publisher object that is recognized as Mutiny's object:

fun mutinyService(id: Long): Uni<Fruit?> {
    GlobalScope.async {
        findById(id)
    }.asUni()
}
Enter fullscreen mode Exit fullscreen mode

Refer to Mutiny's documentation for more informations about coroutines support: Offial documentation
Here there is a complete project that uses eavily the stack described in this article: Smart-home backend services

Discussion (0)

pic
Editor guide