Einführung in die Zukunft in Vavr

Einführung in die Zukunft in Vavr

1. Einführung

Core Java bietet eine grundlegende API für asynchrone Berechnungen -Future.CompletableFuture ist eine der neuesten Implementierungen.

Vavr bietet seine neue funktionale Alternative zurFuture-API. In diesem Artikel werden wir die neue API erläutern und zeigen, wie Sie einige ihrer neuen Funktionen nutzen können.

Weitere Artikel zu Vavr finden Sie unterhere.

2. Maven-Abhängigkeit

DieFuture-API ist in der Vavr Maven-Abhängigkeit enthalten.

Fügen wir es also zu unserenpom.xmlhinzu:


    io.vavr
    vavr
    0.9.2

Wir können die neueste Version der Abhängigkeit vonMaven Central finden.

3. VavrsFuture

DieFuture können sich in einem von zwei Zuständen befinden:

  • Ausstehend - Die Berechnung wird fortgesetzt

  • Abgeschlossen - Die Berechnung wurde erfolgreich mit einem Ergebnis beendet, ist mit einer Ausnahme fehlgeschlagen oder wurde abgebrochen

Der Hauptvorteil gegenüber JavaFuture besteht darin, dass wir problemlos Rückrufe registrieren und Operationen auf nicht blockierende Weise erstellen können.

4. GrundlegendeFuture Operationen

4.1. Asynchrone Berechnungen starten

Lassen Sie uns nun sehen, wie wir mit Vavr asynchrone Berechnungen starten können:

String initialValue = "Welcome to ";
Future resultFuture = Future.of(() -> someComputation());

4.2. Abrufen von Werten aus aFuture

Wir können Werte ausFuture extrahieren, indem wir einfach eine der Methodenget() odergetOrElse() aufrufen:

String result = resultFuture.getOrElse("Failed to get underlying value.");

Der Unterschied zwischenget() undgetOrElse() besteht darin, dassget() die einfachste Lösung ist, währendgetOrElse() es uns ermöglicht, einen Wert eines beliebigen Typs zurückzugeben, falls wir ihn nicht abrufen konnten der Wert innerhalb derFuture.

Es wird empfohlen,getOrElse() zu verwenden, damit wir alle Fehler behandeln können, die beim Abrufen des Werts vonFuture auftreten. For the sake of simplicity, we’ll just use get() in the next few examples.

Beachten Sie, dass die Methodeget()den aktuellen Thread blockiert, wenn auf das Ergebnis gewartet werden muss.

Ein anderer Ansatz besteht darin, die nicht blockierendegetValue()-Methode aufzurufen, dieOption<Try<T>> undwill be empty as long as computation is pending. zurückgibt

Wir können dann das Berechnungsergebnis extrahieren, das sich im ObjektTrybefindet:

Option> futureOption = resultFuture.getValue();
Try futureTry = futureOption.get();
String result = futureTry.get();

Manchmal müssen wir überprüfen, obFuture einen Wert enthält, bevor wir Werte daraus abrufen.

Wir können das einfach tun, indem wir verwenden:

resultFuture.isEmpty();

Es ist wichtig zu beachten, dass die MethodeisEmpty() blockiert - sie blockiert den Thread, bis der Vorgang abgeschlossen ist.

4.3. Ändern der StandardeinstellungExecutorService

Futures verwendenExecutorService, um ihre Berechnungen asynchron auszuführen. Der StandardwertExecutorService istExecutors.newCachedThreadPool().

Wir können weitereExecutorService verwenden, indem wir eine Implementierung unserer Wahl übergeben:

@Test
public void whenChangeExecutorService_thenCorrect() {
    String result = Future.of(newSingleThreadExecutor(), () -> HELLO)
      .getOrElse(error);

    assertThat(result)
      .isEqualTo(HELLO);
}

5. Ausführen von Aktionen nach Abschluss

Die API stellt die MethodeonSuccess() bereit, die eine Aktion ausführt, sobaldFuture erfolgreich abgeschlossen wurde.

In ähnlicher Weise wird die MethodeonFailure() ausgeführt, wennFuture ausfällt.

Sehen wir uns ein kurzes Beispiel an:

Future resultFuture = Future.of(() -> appendData(initialValue))
  .onSuccess(v -> System.out.println("Successfully Completed - Result: " + v))
  .onFailure(v -> System.out.println("Failed - Result: " + v));

Die MethodeonComplete() akzeptiert eine Aktion, die ausgeführt werden soll, sobaldFuture ihre Ausführung abgeschlossen hat, unabhängig davon, obFuture erfolgreich war oder nicht. Die MethodeandThen() ähneltonComplete() - sie garantiert nur, dass die Rückrufe in einer bestimmten Reihenfolge ausgeführt werden:

Future resultFuture = Future.of(() -> appendData(initialValue))
  .andThen(finalResult -> System.out.println("Completed - 1: " + finalResult))
  .andThen(finalResult -> System.out.println("Completed - 2: " + finalResult));

6. Nützliche Operationen fürFutures

6.1. Blockieren des aktuellen Threads

Die Methodeawait() hat zwei Fälle:

  • WennFuture ansteht, wird der aktuelle Thread blockiert, bis die Zukunft abgeschlossen ist

  • WennFuture abgeschlossen ist, wird es sofort beendet

Die Verwendung dieser Methode ist einfach:

resultFuture.await();

6.2. Berechnung abbrechen

Wir können die Berechnung jederzeit abbrechen:

resultFuture.cancel();

6.3. Abrufen der zugrunde liegendenExecutorService

Um dieExecutorService zu erhalten, die vonFuture verwendet werden, können wir einfachexecutorService() aufrufen:

resultFuture.executorService();

6.4. Erhalten einesThrowable von einem fehlgeschlagenenFuture

Wir können dies mit der MethodegetCause() tun, die dieThrowable zurückgibt, die in einio.vavr.control.Option-Objekt eingeschlossen sind.

Wir können später dieThrowable aus demOption-Objekt extrahieren:

@Test
public void whenDivideByZero_thenGetThrowable2() {
    Future resultFuture = Future.of(() -> 10 / 0)
      .await();

    assertThat(resultFuture.getCause().get().getMessage())
      .isEqualTo("/ by zero");
}

Zusätzlich können wir unsere Instanz mit der Methodefailed() in eineFuture konvertieren, die eineThrowable Instanz enthält:

@Test
public void whenDivideByZero_thenGetThrowable1() {
    Future resultFuture = Future.of(() -> 10 / 0);

    assertThatThrownBy(resultFuture::get)
      .isInstanceOf(ArithmeticException.class);
}

6.5. isCompleted(), isSuccess(), undisFailure()

Diese Methoden sind so ziemlich selbsterklärend. Sie prüfen, ob einFutureabgeschlossen ist, ob es erfolgreich oder mit einem Fehler abgeschlossen wurde. Natürlich geben alleboolean Werte zurück.

Wir werden diese Methoden mit dem vorherigen Beispiel verwenden:

@Test
public void whenDivideByZero_thenCorrect() {
    Future resultFuture = Future.of(() -> 10 / 0)
      .await();

    assertThat(resultFuture.isCompleted()).isTrue();
    assertThat(resultFuture.isSuccess()).isFalse();
    assertThat(resultFuture.isFailure()).isTrue();
}

6.6. Anwenden von Berechnungen auf eine Zukunft

Die Methodemap() ermöglicht es uns, eine Berechnung auf eine ausstehendeFuture: anzuwenden

@Test
public void whenCallMap_thenCorrect() {
    Future futureResult = Future.of(() -> "from example")
      .map(a -> "Hello " + a)
      .await();

    assertThat(futureResult.get())
      .isEqualTo("Hello from example");
}

Wenn wir eine Funktion übergeben, dieFuture an die Methodemap() zurückgibt, können wir eine verschachtelteFuture-Struktur erhalten. Um dies zu vermeiden, können wir dieflatMap()-Methode nutzen:

@Test
public void whenCallFlatMap_thenCorrect() {
    Future futureMap = Future.of(() -> 1)
      .flatMap((i) -> Future.of(() -> "Hello: " + i));

    assertThat(futureMap.get()).isEqualTo("Hello: 1");
}



6.7. Futures transformieren

Mit der MethodetransformValue() kann eine Berechnung aufFuture angewendet und der darin enthaltene Wert in einen anderen Wert desselben Typs oder eines anderen Typs geändert werden:

@Test
public void whenTransform_thenCorrect() {
    Future future = Future.of(() -> 5)
      .transformValue(result -> Try.of(() -> HELLO + result.get()));

    assertThat(future.get()).isEqualTo(HELLO + 5);
}



6.8. ZippingFutures

Die API bietet die Methodezip(), mit derFutures zu Tupeln zusammengefasst werden. Ein Tupel ist eine Sammlung mehrerer Elemente, die möglicherweise miteinander in Beziehung stehen oder nicht. Sie können auch von unterschiedlicher Art sein. Sehen wir uns ein kurzes Beispiel an:

@Test
public void whenCallZip_thenCorrect() {
    Future f1 = Future.of(() -> "hello1");
    Future f2 = Future.of(() -> "hello2");

    assertThat(f1.zip(f2).get())
      .isEqualTo(Tuple.of("hello1", "hello2"));
}

Hierbei ist zu beachten, dass die resultierendenFuture ausstehen, solange noch mindestens eine der BasisFutures aussteht.

6.9. Umrechnung zwischenFutures undCompletableFutures

Die API unterstützt die Integration mitjava.util.CompletableFuture. Daher können wirFuture leicht inCompletableFuture konvertieren, wenn wir Operationen ausführen möchten, die nur von der Java-Kern-API unterstützt werden.

Mal sehen, wie wir das machen können:

@Test
public void whenConvertToCompletableFuture_thenCorrect()
  throws Exception {

    CompletableFuture convertedFuture = Future.of(() -> HELLO)
      .toCompletableFuture();

    assertThat(convertedFuture.get())
      .isEqualTo(HELLO);
}

Wir können auchCompletableFuture inFuture mit der MethodefromCompletableFuture() konvertieren.

6.10. Ausnahmebehandlung

Nach dem Ausfall vonFuture können wir den Fehler auf verschiedene Arten behandeln.

Beispielsweise können wir die Methoderecover() verwenden, um ein anderes Ergebnis zurückzugeben, z. B. eine Fehlermeldung:

@Test
public void whenFutureFails_thenGetErrorMessage() {
    Future future = Future.of(() -> "Hello".substring(-1))
      .recover(x -> "fallback value");

    assertThat(future.get())
      .isEqualTo("fallback value");
}

Oder wir können das Ergebnis einer weiteren Berechnung vonFuturemitrecoverWith() zurückgeben:

@Test
public void whenFutureFails_thenGetAnotherFuture() {
    Future future = Future.of(() -> "Hello".substring(-1))
      .recoverWith(x -> Future.of(() -> "fallback value"));

    assertThat(future.get())
      .isEqualTo("fallback value");
}

Die MethodefallbackTo() ist eine weitere Möglichkeit, Fehler zu behandeln. Es wird einFuture aufgerufen und akzeptiert ein weiteresFuture als Parameter.

Wenn das ersteFuture erfolgreich ist, gibt es sein Ergebnis zurück. Wenn andernfalls das zweiteFuture erfolgreich ist, gibt es sein Ergebnis zurück. Wenn beideFutures fehlschlagen, gibt diefailed()-MethodeFuture vonThrowable zurück, was den Fehler der erstenFuture enthält:

@Test
public void whenBothFuturesFail_thenGetErrorMessage() {
    Future f1 = Future.of(() -> "Hello".substring(-1));
    Future f2 = Future.of(() -> "Hello".substring(-2));

    Future errorMessageFuture = f1.fallbackTo(f2);
    Future errorMessage = errorMessageFuture.failed();

    assertThat(
      errorMessage.get().getMessage())
      .isEqualTo("String index out of range: -1");
}

7. Fazit

In diesem Artikel haben wir gesehen, was einFutureist, und einige seiner wichtigen Konzepte kennengelernt. Wir haben auch einige der Funktionen der API anhand einiger praktischer Beispiele erläutert.

Die Vollversion des Codes ist inover on GitHub verfügbar.