Voeux scriptés avec CompletableFuture

En ce début d'année 2019, j'exprimais sur LinkedIn mes buts pour la nouvelle année, sous la forme d'un script Groovy utilisant l'API CompletableFuture de Java 8 :

1
2
3
4
5
6
7
8
import java.util.concurrent.CompletableFuture

def newYearGoals = ['projects', 'challenges', 'learning', '...']
CompletableFuture.supplyAsync { 2019 }
    .thenAccept {
        newYearGoals.each { println "$it done!" }
    }
    .get()

L'intérêt immédiat d'utiliser des scripts Groovy est de pouvoir tester facilement des API et des librairies externes ; dans ce dernier cas, on pourra recourir au système Grape du langage Groovy.

Pour revenir à l'API CompletableFuture, voici un nouvel exemple similaire mais un peu plus détaillé, suivi de quelques commentaires :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import java.util.concurrent.CompletableFuture
import java.util.function.Supplier

def task = {
    def newYearGoals = ['projects', 'challenges', 'learning']
    newYearGoals.each {
        println "Start $it..."
        sleep 500
    }
}

def future = CompletableFuture.supplyAsync(task as Supplier)
    .thenAccept {
        println "$it done!"
    }

// Do some other work
4.times {
    println "isDone: $future.done"
    sleep 600
}

future.get()
println "Finished!"

Dans cet exemple, on fait apparaitre de manière plus claire la tâche asynchrone à exécuter, par la Closure task (pouvant être potentiellement longue), qui est transtypée plus loin en l'interface fonctionnelle Supplier dans l'appel à CompletableFuture.supplyAsync ; cet appel est chainé avec celui de thenAccept dans le but d'exécuter du code spécifique lorsque l'exécution de la tâche est terminée.
Ceci donne un exemple de définition de callback sur un CompletableFuture, qui permet d'exécuter du code une fois la tâche asynchrone terminée.

Plus loin, la référence future de type (dynamique) CompletableFuture nous permet ensuite d'indiquer, à plusieurs reprises dans la boucle apportée par 4.times, si la tâche asynchrone est terminée, en appelant la méthode isDone().

Pour terminer, l'appel future.get() assure que la tâche est bien terminée avant de poursuivre, ce qui fait que Finished! sera toujours le dernier message affiché dans la console :

1
2
3
4
5
6
7
8
9
Start projects...
isDone: false
Start challenges...
isDone: false
Start learning...
isDone: false
[projects, challenges, learning] done!
isDone: true
Finished!