2015-11-30 8 views
13

Se avere 2 CompletionStages io possa combinare con thenCombine metodo:Come combinare 3 o più livelli di completamento?

CompletionStage<A> aCompletionStage = getA(); 
CompletionStage<B> bCompletionStage = getB(); 
CompletionStage<Combined> combinedCompletionStage = 
    aCompletionStage.thenCombine(bCompletionStage, (aData, bData) -> combine(aData, bData)); 

Se ho 3 o più CompletionStages posso fare una catena di thenCombine metodi, ma devo usare oggetti temporanei per passare risultati. Ad esempio, ecco una soluzione utilizzando Pair e Triple dal pacchetto org.apache.commons.lang3.tuple:

CompletionStage<A> aCompletionStage = getA(); 
CompletionStage<B> bCompletionStage = getB(); 
CompletionStage<C> cCompletionStage = getC(); 
CompletionStage<D> dCompletionStage = getD(); 

CompletionStage<Combined> combinedDataCompletionStage = 
     aCompletionStage.thenCombine(bCompletionStage, (Pair::of)) 
       .thenCombine(cCompletionStage, (ab, c) -> 
         Triple.of(ab.getLeft(), ab.getRight(), c)) 
       .thenCombine(dCompletionStage, (abc, d) -> 
         combine(abc.getLeft(), abc.getMiddle(), abc.getRight(), d)); 

C'è un modo migliore per combinare i risultati provenienti da più CompletionStages?

+2

non capisco ciò che si sta cercando di fare. Qual è il tuo metodo 'combine'? Qual è il suo scopo? Cosa vuoi fare con 'CompletionStage's? Chi è il consumatore alla fine? Quale dovrebbe essere il risultato che ricevono? –

+0

@SotiriosDelimanolis, 'combine' è una funzione che accetta 4 parametri e restituisce 1 di tipo' Combinato'. Il consumatore può essere ad esempio un'altra funzione che assume 'Combined' come parametro e restituisce un altro' CompletionStage', che viene invocato usando 'combinedDataCompletionStage.thenCompose (...)'. –

risposta

17

L'unico modo per combinare più stadi che si adatta bene a un numero crescente di fasi, è utilizzare CompletableFuture. Se i CompletionStage s non sono CompletableFuture s si può ancora convertire usando .toCompletableFuture():

CompletableFuture<A> aCompletionStage = getA().toCompletableFuture(); 
CompletableFuture<B> bCompletionStage = getB().toCompletableFuture(); 
CompletableFuture<C> cCompletionStage = getC().toCompletableFuture(); 
CompletableFuture<D> dCompletionStage = getD().toCompletableFuture(); 

CompletionStage<Combined> combinedDataCompletionStage = CompletableFuture.allOf(
    aCompletionStage, bCompletionStage, cCompletionStage, dCompletionStage) 
    .thenApply(ignoredVoid -> combine(
     aCompletionStage.join(), bCompletionStage.join(), 
     cCompletionStage.join(), dCompletionStage.join())); 

Questo contiene più boilerplate che unire due fasi tramite thenCombine ma il boilerplate non cresce quando si aggiungono più fasi ad esso.


Si noti che anche con il vostro approccio originale thenCombine, non hai bisogno di un Triple, un Pair è sufficiente:

CompletionStage<Combined> combinedDataCompletionStage = 
    aCompletionStage.thenCombine(bCompletionStage, (Pair::of)).thenCombine(
     cCompletionStage.thenCombine(dCompletionStage, Pair::of), 
     (ab, cd) -> combine(ab.getLeft(), ab.getRight(), cd.getLeft(), cd.getRight())); 

Eppure, non scala bene se si desidera combinare più stadi.


un in-tra la soluzione (per quanto riguarda la complessità) potrebbe essere:

CompletionStage<Combined> combinedDataCompletionStage = aCompletionStage.thenCompose(
    a -> bCompletionStage.thenCompose(b -> cCompletionStage.thenCompose(
     c -> dCompletionStage.thenApply(d -> combine(a, b, c, d))))); 

Questo è più semplice nella sua struttura, ma ancora non scala bene con più più stadi.

2

Holger's third answer può essere fatto un po 'più corta:

CompletionStage<Combined> combinedDataCompletionStage = aCompletionStage.thenCompose(
    a -> bCompletionStage.thenCompose(
     b -> cCompletionStage.thenCombine(dCompletionStage, 
      (c, d) -> combine(a, b, c, d))));