2016-11-19 3 views
1

Ich habe eine Frage bezüglich CompletableFuture in Java. Ich warte auf den Abschluss einer CompletableFuture, und abhängig vom empfangenen Ergebnis möchte ich entweder eine neue Aufgabe aufrufen und darauf warten, dass CompleableFuture abgeschlossen wird oder etwas anderes tut. Ich bin nicht glücklich mit meiner Lösung, da es so viele Rückrufe gibt und es ist anders zu lesen. Könnten Sie mir helfen, meinen Code zu verbessern?Java CompleableFuture: Callback-Hölle vermeiden

final CompletableFuture<String> future = new CompletableFuture<>(); 

final ActorRef processCheckActor = actorSystem.actorOf(
    springExtension.props("ProcessCheckActor"), "processCheckActor-" + new Random().nextInt()); 

final CompletableFuture<Object> checkResponse = 
    PatternsCS.ask(processCheckActor, new ProcessToCheckMessage(processId), TIMEOUT) 
     .toCompletableFuture(); 

checkResponse.thenAccept(obj -> { 
    final ProcessCheckResponseMessage msg = (ProcessCheckResponseMessage) obj; 
    if (msg.isCorrect()) { 
    final CompletableFuture<Object> response = 
     PatternsCS.ask(processSupervisorActor, new ProcessStartMessage(processId), TIMEOUT) 
      .toCompletableFuture(); 

    response.thenAccept(obj2 -> { 
     future.complete("yes"); 
    }); 
    } else { 
    future.complete("no"); 
    } 
}); 

Antwort

2

Zunächst einmal sollten Sie eine CompletableFuture<Object> Schaffung vermeiden. Der generische Typ sollte der Typ sein, den Ihre Funktion zurückgibt (CompletableFuture<ProcessCheckResponseMessage> in Ihrem Fall). Auf diese Weise brauchen Sie die Besetzung nicht.

Ich würde vorschlagen, thenApply anstatt thenAccept zu verwenden. Dadurch wird die zweite CompletableFuture für Sie erstellt, sodass Sie die Deklaration nicht mehr in der ersten Zeile benötigen.

Schließlich sollten Sie als allgemeine Regel zweimal über Multi-Line-Lambdas nachdenken und auf jeden Fall verschachtelte Lambdas vermeiden. Sie sollten stattdessen eine neue Methode für diese Lambdas erstellen.

1

Meine 2 Cent mit Beispielcode, um Ihr Rückruf-Szenario zu helfen.

Ich schrieb 3 Funktionen: testFunction, getResponseMessage und getResponseString.

  • testFunction ist die Hauptfunktion.
  • getResponseMessage zur Haupt testFunction durch thenApply
  • getResponseString verkettet ist am Ende durch thenCompose verkettet.

Der Trick ist, an die Kette mehrere kleinere Funktionen durch die Funktionen höherer Ordnung wie thenApply, thenCompose, thenCombine usw.

public CompletableFuture<String> testFunction() { 

    Future<Object> fut = Patterns.ask(getContext().actorSelection("actor1"), new ProcessToCheckMessage(1), 10); 

    return FutureConverters.toJava(fut).toCompletableFuture() 

      .thenApply(obj -> getResponseMessage(obj)) 

      .thenCompose(processCheckResponseMessage -> getResponseString(processCheckResponseMessage)); 
} 

public ProcessCheckResponseMessage getResponseMessage(Object obj) { 
    if (obj instanceof ProcessCheckResponseMessage) { 
     return (ProcessCheckResponseMessage) obj; 
    } else { 
     throw new RuntimeException("unexpected data"); 
    } 
} 

public CompletableFuture<String> getResponseString(ProcessCheckResponseMessage processCheckResponseMessage) { 
    if (processCheckResponseMessage.isCorrect()) { 
     Future<Object> rest = Patterns.ask(getContext().actorSelection("actor2"), new ProcessStartMessage(1), 10); 
     return FutureConverters.toJava(rest).toCompletableFuture().thenApply(obj -> "yes"); 
    } else { 
     return CompletableFuture.completedFuture("no"); 
    } 
} 
Verwandte Themen