Будут ли .exceptionally () ловить исключения, выбрасываемые из вложенных фьючерсов? Или где правильно поставить .exceptionally () - PullRequest
1 голос
/ 26 апреля 2019
foo.thenCompose(fooResponse -> {
  ...
  return bar.thenCompose(barResponse -> {
    ...
  });
}).exceptionally(e -> {
  ...
});

Будет ли это .exceptionally() также отлавливать исключения, выбрасываемые из вложенной bar.thenCompose лямбды? Или мне нужно написать это:

foo.thenCompose(fooResponse -> {
  ...
  return bar.thenCompose(barResponse -> {
    ...
  }).exceptionally(nestedE -> {
    ...
  });
}).exceptionally(e -> {
  ...
});

Тогда снова бросить?

Ответы [ 2 ]

4 голосов
/ 29 апреля 2019

Одного exceptionally в конце достаточно, чтобы заменить любое бросаемое значение альтернативным обычным значением результата, по крайней мере, для полученной им конечной стадии, но стоит очистить настрой, который привел к этому вопросу.

exceptionally не перехватывает никаких исключений, и фьючерсов вложенных тоже нет.Важно понимать, что все методы, определенные CompletionStage, создают новый этап завершения, на завершение которого будет влиять контракт конкретного метода, но никогда не влияют на этап завершения,метод был вызван.

Поэтому, когда вы используете exceptionally, в нем задействовано два фьючерса: тот, на который вы вызываете исключительно, и новое будущее, возвращаемое exceptionally.Контракт заключается в том, что последний будет завершен с тем же значением, что и первый, в случае обычного завершения, но с результатом оценки функции, если первый был выполнен исключительно.

Так что, когда вывыполнить

for(int run = 0; run < 4; run++) {
    CompletableFuture<String> stage1 = new CompletableFuture<>();
    CompletableFuture<String> stage2 = stage1.exceptionally(t -> "alternative result");

    if(run > 1) stage2.cancel(false);

    if((run&1) == 0) stage1.complete("ordinary result");
    else stage1.completeExceptionally(new IllegalStateException("some failure"));

    stage1.whenComplete((v,t) ->
        System.out.println("stage1: "+(t!=null? "failure "+t: "value "+v)));
    stage2.whenComplete((v,t) ->
        System.out.println("stage2: "+(t!=null? "failure "+t: "value "+v)));
    System.out.println();
}

, он напечатает:

stage1: value ordinary result
stage2: value ordinary result

stage1: failure java.lang.IllegalStateException: some failure
stage2: value alternative result

stage1: value ordinary result
stage2: failure java.util.concurrent.CancellationException

stage1: failure java.lang.IllegalStateException: some failure
stage2: failure java.util.concurrent.CancellationException

, показывая, что первый этап всегда отражает результат нашего явного завершения, независимо от того, что происходит со вторым этапом.Таким образом, exceptionally не улавливает исключение, исключительное завершение предыдущего этапа никогда не изменяется, все, что он делает, это определяет завершение нового этапа.

Так что, если stage1 было результатомскажем, stage0.thenCompose(x -> someOtherStage) звоните, это не имеет значения для отношений между stage1 и stage2.Все, что имеет значение, это завершение stage1.

  1. Если stage0 выполнено исключительно, оно попытается завершить stage1 исключительно
  2. Если stage0 выполненосо значением и функцией выдает исключение, она будет пытаться завершить stage1 исключительно
  3. Если stage0 завершается со значением, и функция возвращает этап (someOtherStage), который был или будетбудет завершено в исключительных случаях, оно будет пытаться завершить stage1 исключительно
  4. Если stage0 завершается со значением, и функция возвращает этап (someOtherStage), который был или будет завершен со значением,он попытается завершить stage1 с этим значением

Обратите внимание, что нет вложенности, someOtherStage может быть недавно построенным или уже существующим этапом и может использоваться в других местах.,Поскольку цепочка всегда создает новые этапы, не затрагивая существующие, эти другие места не будут затронуты чем-либо, что происходит здесь.

Обратите внимание далее на термин «попытка завершить», так как мы все еще можем вызвать complete,completeExceptionally или cancel на stage1 до этой попытки.Для stage2 не имеет значения, каким образом произошло завершение, все, что имеет значение, это результат.

Так что, если попытки любого из случаев с 1. до 3. завершить stage1 Исключительно, успешно, будет попытка завершить stage2 с результатом функции, переданной exceptionally.В случае 4, если попытка завершить stage1 значением будет успешной, будет попытка завершить stage2 этим значением.

Чтобы продемонстрировать несущественность истории предыдущего этапа, если мы используем

CompletableFuture<String> stage1 = new CompletableFuture<>();
CompletableFuture<String> stage2 = stage1.thenCompose(s -> new CompletableFuture<>());
CompletableFuture<String> stage3 = stage2.exceptionally(t -> "alternative result");

stage1.complete("ordinary result"); // you can omit this line if you want
stage2.completeExceptionally(new IllegalStateException("some failure"));

stage3.whenComplete((v,t) ->
    System.out.println("stage3: "+(t!=null? "failure "+t: "value "+v)));

Он напечатает stage3: value alternative result из-за того, что stage2 был завершен исключительно, история завершения была совершенно неактуальной.Оператор stage1.complete("ordinary result"); вызовет оценку функции, возвращающей новый CompletableFuture, которая никогда не будет завершена, следовательно, не повлияет на результат.Если мы пропустим эту строку, stage1 никогда не будет завершено, и функция никогда не будет оценена, следовательно, «вложенная» стадия никогда не будет создана, но, как сказано, эта история не имеет значения для stage2.

Так что если вы последний вПризыв к этапам завершения цепочки - exceptionally(function), он вернет новый этап, который всегда будет завершен со значением, либо из предыдущего этапа, либо из function, независимо от того, как выглядит график зависимостей перед ними.Если function сам не сгенерирует исключение или кто-то не вызовет для него один из явных методов завершения, например cancel.

0 голосов
/ 26 апреля 2019

Да, с помощью «исключительного» метода вы можете обрабатывать вложенные CompletableFuture Первый пример будет работать

...