web-dev-qa-db-fra.com

Est-il possible d'utiliser Java 8 API Streams pour le traitement asynchrone?

J'ai joué avec CompletionStage/CompletableFuture en Java 8 afin de faire un traitement asynchrone, qui fonctionne assez bien. Cependant, parfois je veux qu'une étape effectue un traitement asynchrone d'un itérateur/flux de articles, et il ne semble pas y avoir de moyen de le faire.

Plus précisément, Stream.forEach () a la sémantique qu'après l'appel, tous les éléments ont été traités. Je voudrais la même chose, mais avec un CompletionStage à la place, par exemple:

CompletionStage<Void> done = stream.forEach(...);
done.thenRun(...);

Si le Stream est soutenu par un résultat de streaming asynchrone, ce serait bien mieux que d'attendre qu'il soit complet dans le code ci-dessus lui-même.

Est-il possible de construire cela avec l'API Java 8 API actuelle? Contournements?

30
Rickard Öberg

Pour autant que je sache, l'API streams ne prend pas en charge le traitement des événements asynchrones. On dirait que vous voulez quelque chose comme Reactive Extensions for .NET, et il y a un port Java appelé RxJava , créé par Netflix.

RxJava prend en charge bon nombre des mêmes opérations de haut niveau que Java 8 flux (tels que la carte et le filtre) et est asynchrone.

Mise à jour : Il y a maintenant une initiative flux réactifs en cours, et il semble que JDK 9 inclura un support pour au moins en partie si la classe Flow .

22
Soulman

Comme @KarolKrol l'a mentionné, vous pouvez le faire avec un flux de CompletableFuture.

Il existe une bibliothèque qui s'appuie sur les flux JDK8 pour faciliter le travail avec les flux de CompletableFuture appelés cyclops-react .

Pour composer vos flux, vous pouvez utiliser l'API ike promesse fluide de cyclops-react ou vous pouvez utiliser Stages de simple-react.

7
Adam Gent

cyclops-react (Je suis l'auteur de cette bibliothèque), fournit une classe StreamUtils pour le traitement des Streams. L'une des fonctions qu'il fournit est futureOperations, qui donne accès aux opérations standard du terminal Stream (et puis à certaines) avec une torsion - le Stream est exécuté de manière asynchrone et le résultat est renvoyé dans un CompletableFuture. .par exemple

 Stream<Integer> stream = Stream.of(1,2,3,4,5,6)
                                       .map(i->i+2);
 CompletableFuture<List<Integer>> asyncResult =  StreamUtils.futureOperations(stream,
                                             Executors.newFixedThreadPool(1))
                                       .collect(Collectors.toList());

Il existe également une classe de réactivité ReactiveSeq qui encapsule Stream et fournit les mêmes fonctionnalités, avec une belle API fluide

 CompletableFuture<List<Integer>> asyncResult = ReactiveSeq.of(1,2,3,4,5,6)
                                       .map(i->i+2)
                                       .futureOperations(
                                             Executors.newFixedThreadPool(1))
                                       .collect(Collectors.toList());

Comme Adam l'a souligné cyclops-react FutureStreams est conçu pour traiter les données de manière asynchrone (en mélangeant ensemble Futures et Streams) - il est particulièrement adapté aux opérations multithread qui impliquent le blocage des E/S (comme la lecture fichiers, faire des appels db, faire des appels de repos, etc.).

3
John McClean

Il est possible de produire un flux, de mapper chaque élément sur CompletionStage et de collecter les résultats à l'aide de CompletionStage.thenCombine(), mais le code résultant ne sera pas plus lisible que simple à utiliser comme ceci.

    CompletionStage<Collection<Result>> collectionStage =
        CompletableFuture.completedFuture(
            new LinkedList<>()
        );

    for (Request request : requests) {
        CompletionStage<Result> resultStage = performRequest(request);
        collectionStage = collectionStage.thenCombine(
            resultStage,
            (collection, result) -> {
                collection.add(result);
                return collection;
            }
        );
    }

    return collectionStage;

Cet exemple peut être facilement transformé en fonctionnel pour ne pas perdre la lisibilité. Mais l'utilisation de reduce ou collect de stream nécessite un code supplémentaire pas si fin.

Mettre à jour: CompletableFuture.allOf et CompletableFuture.join fournit un autre moyen plus lisible de transformer la collecte des résultats futurs en future collecte de résultats.

1