DEV Community

Mathieu Marchadour for Avalon Lab

Posted on

3 2

Utiliser les Futures avec Vavr

Introduction

Dans le cadre d’un projet chez l’un de nos clients, on nous a chargé de réaliser une API. Celle-ci avait la responsabilité d’agréger différentes données venant de plusieurs API.

À des fins de performance, nous avons choisi de paralléliser les appels et d’utiliser les Futures.


Présentation

La librairie Vavr est utilisable à partir de la version 8 de Java.

Cette librairie a pour but d’améliorer le coté programmation fonctionnelle de Java 8 en ajoutant notamment des types de données immuables et des structures de contrôles orientés fonctionnelles.

C'est parmi ces types de données immuables que nous trouvons notre Future, objet de cet article.

Une Future va permettre de réaliser une action dont le résultat sera disponible dans le futur.

Lorsque l'action de la Future est terminée, trois états de celle-ci sont possibles :

  • completed: lorsque la Future s'est terminée

  • success: lorsque la Future s'est terminée avec succès

  • failure: lorsque la Future s'est terminée en erreur

Il est possible d'ajouter des callbacks afin d'intercepter ces trois états.

Chez notre client, cela permettait de lancer X actions (Futures) permettant de récupérer des données, attendre le résultat de chacune et agréger toutes les données. Pour mettre en place ceci, nous avons créé une liste de Futures, les avons packagées dans une séquence. Nous devons attendre le résultat de toutes les Futures et avons donc bloqué les différents Thread grâce à la méthode .await(). Ensuite, la méthode .get() nous permet d’obtenir le résultat de la Future.


Illustration : la course d'animaux

Afin de lancer la course et de vous présenter les Futures, nous allons réaliser un test unitaire.

Création de la classe Java Animal avec son nom, sa famille, son type et vitesse moyenne. L'annotation @Data vient du projet Lombok : une librairie de génération de code. Ce projet fera l'objet d'un autre article.

package com.avalonlab.vavr.bean;
import lombok.Data;
@Data
public class Animal {
private String nom;
private TypeAnimal type;
private String famille;
private int vitesse;
public Animal(String nom, TypeAnimal type, String famille, int vitesse) {
this.nom = nom;
this.type = type;
this.famille = famille;
this.vitesse = vitesse;
}
}
view raw Animal.java hosted with ❤ by GitHub
package com.avalonlab.vavr.bean;
public enum TypeAnimal {
CHIEN,
CHAT,
LAPIN,
SOURIS,
ANE
}
view raw TypeAnimal.java hosted with ❤ by GitHub

Ici, nous initialisons 5 animaux qui sont de famille et de vitesse différentes.

package com.avalonlab.vavr;
import com.avalonlab.vavr.bean.Animal;
import com.avalonlab.vavr.bean.TypeAnimal;
import org.junit.Before;
import io.vavr.collection.List;
public class VavrTest {
protected List<Animal> animaux;
protected Animal chien;
protected Animal chat;
protected Animal lapin;
protected Animal souris;
protected Animal ane;
@Before
public void init() {
chien = new Animal("Idéfix", TypeAnimal.CHIEN, "Canidés", 60);
chat = new Animal("Garfield", TypeAnimal.CHAT, "Félin", 38);
lapin = new Animal("Panpan", TypeAnimal.LAPIN, "Léporidés", 30);
souris = new Animal("Ratatouille", TypeAnimal.SOURIS, "Rongeurs", 10);
ane = new Animal("Bourriquet", TypeAnimal.ANE, "Équidés", 15);
animaux = List.of(chien, chat, lapin, souris, ane);
}
}
view raw VavrTest.java hosted with ❤ by GitHub

Le test unitaire:

package com.avalonlab.vavr;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import com.avalonlab.vavr.bean.Animal;
import com.avalonlab.vavr.bean.TypeAnimal;
import org.junit.Test;
import io.vavr.collection.List;
import io.vavr.collection.Seq;
import io.vavr.concurrent.Future;
import static org.assertj.core.api.Assertions.assertThat;
public class VavrFutureTest extends VavrTest {
private int distanceDeLaCoursEnM = 100;
private List<String> resultats = List.empty();
@Test
public void lancerLaCourse() {
// On prépare les futures des animaux.
Future<Animal> futureDuChien = preparerUnAnimal(chien);
Future<Animal> futureDuChat = preparerUnAnimal(chat);
Future<Animal> futureDuLapin = preparerUnAnimal(lapin);
Future<Animal> futureDeLaSouris = preparerUnAnimal(souris);
Future<Animal> futureDeLane = preparerUnAnimal(ane);
// On ajout les futures des animaux dans un tableau.
List<Future<Animal>> futures = List.of(futureDuChat, futureDuChien, futureDuLapin, futureDeLaSouris, futureDeLane);
// Lorsque les animaux arriveront à la ligne d'arrivée, on inscrit l'animal sur le tableau des resultats.
futures.forEach(future -> {
future.onComplete(resultat -> inscrireLeResultat(resultat.get()));
});
// Initialisation de la course
Future<Seq<Object>> toutesLesFutures = Future.sequence(futures);
// Lancement de la course et attente que tous les animaux ont terminés la course
toutesLesFutures.await();
System.out.println(getHeure() + " : Course terminée.");
assertThat(resultats).containsExactly(TypeAnimal.CHIEN.name(), TypeAnimal.CHAT.name(), TypeAnimal.LAPIN.name(), TypeAnimal.ANE.name(), TypeAnimal.SOURIS.name());
}
private Future<Animal> preparerUnAnimal(Animal animal) {
Future<Animal> future = Future.of(() -> {
System.out.println(getHeure() + " : Préparation de l'animal " + animal.getNom());
long chronoEnSeconde = calculerChronoEnSeconde(animal, distanceDeLaCoursEnM);
Thread.sleep(chronoEnSeconde * 1000);
return animal;
});
return future;
}
private void inscrireLeResultat(Animal animal) {
resultats.append(animal.getType().name());
long chronoEnSeconde = calculerChronoEnSeconde(animal, distanceDeLaCoursEnM);
System.out.println(getHeure() + " : L'animal " + animal.getNom() + " a fini la course en " + chronoEnSeconde + " secondes.");
}
private long calculerChronoEnSeconde(Animal animal, int distanceEnM) {
double chronoEnHeure = 3.6 * distanceEnM / animal.getVitesse();
return (long) chronoEnHeure;
}
private String getHeure(){
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");
return now.format(formatter);
}
}

Résultat du test unitaire :

Conclusion

Grâce à ce test unitaire, nous avons utilisé la librairie Vavr, disponible à partir de Java 8, afin de paralléliser des traitements. Cette librairie ne permet pas uniquement l’utilisation de Future mais elle implémente aussi des Collections, List, Map, Option, Try …

La documentation de Vavr est disponible ci-dessous afin d’approfondir cette librairie.

Documentation

Site officiel Vavr

Introduction to Future in Vavr | Baeldung

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay