Как вызвать несколько завершаемых будущих одновременно после завершения основного завершаемого будущего

#java-8 #completable-future

#java-8 #завершаемое будущее

Вопрос:

Как улучшить функцию ниже, где getB, getC, getD имеют зависимость от A и должны ждать завершения перед вызовом. Однако я хочу вызвать B C D одновременно после завершения.

Спасибо за всю помощь

примечание: все dbService возвращают завершаемое будущее

 CompletableFuture<List<A>> a= dbService.getA(request);
a.thenApply(a-> {
                try {
                    return dbService.getB(a);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    return null;
                }
            })
            .thenAccept (result->{
                //do something with B
            });

a.thenApply(a-> {
                try {
                    return dbService.getC(a);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    return null;
                }
            })
            .thenAccept (result->{
                //do something with C
            });

a.thenApply(a-> {
                try {
                    return dbService.getD(a);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                    return null;
                }
            })
            .thenAccept (result->{
                //do something with D
            });

  

Комментарии:

1. Использовать thenApplyAsync ?

2. Вы имеете в виду объединение всего с applyAsync ? если да, то как я могу повторно ввести параметры A

3. Что вы имеете в виду? Просто замените все три thenApply на thenApplyAsync .

4. я имею в виду, есть ли возможность объединить все три в одной функции? Спасибо

5. Вы сказали, что хотите, чтобы B, C и D выполнялись одновременно. Так что нет, у вас не может быть этого и этих одновременных вызовов в одной функции. В этом тоже нет смысла. Если вы хотите упростить код, исправьте метод dbService так, чтобы он не выдавал InterruptedException , так как это противоречит возвращению объекта, представляющего асинхронную операцию, но объявляет выдачу исключения, указывающего на операцию ожидания.

Ответ №1:

Если вас не волнует возврат B, C, D, то:

 CompletionStage<List<A>> aFuture = dbService.getA(request);
aFuture.whenCompleteAsync((a, ex) -> {
    if (ex != null) {
        dbService.getB(a);
        // ...
    } else {
        // ...
    }
});
aFuture.whenCompleteAsync((a, ex) -> {
    if (ex != null) {
        dbService.getC(a);
        // ...
    } else {
        // ...
    }
});
aFuture.whenCompleteAsync((a, ex) -> {
    if (ex != null) {
        dbService.getD(a);
        // ...
    } else {
        // ...
    }
});
  

Ваша программа вернется к основному циклу событий, как только dbService.getA(request) начнется, т. е. еще до его завершения. И когда это завершается, каждый из 3 блоков выполняется (из-за того, что они были поставлены в очередь whenComplete ) в разных потоках (отличных от основного и друг от друга, из-за Async ).

Если вы действительно хотите что-то сделать с возвратами B, C и D, то:

 CompletionStage<List<A>> aFuture = dbService.getA(request);

CompletionStage<B> bFuture = aFuture.thenApplyAsync(a -> {
    return dbService.getB(a);
});
CompletionStage<C> cFuture = aFuture.thenApplyAsync(a -> {
    return dbService.getC(a);
});
CompletionStage<D> dFuture = aFuture.thenApplyAsync(a -> {
    return dbService.getD(a);
});

CompletionStage<Something> sFuture = bFuture.thenCompose(b -> {
    cFuture.thenCompose(c -> {
        dFuture.thenApply(d -> {
            // do something with b, c, d
            return new Something();
        });
    });
});
 
  

Вы можете сократить это с помощью thenCombine , который похож на thenApply , который ожидает 2 фьючерса:

 CompletionStage<List<A>> aFuture = dbService.getA(request);

CompletionStage<B> bFuture = aFuture.thenApplyAsync(a -> {
    return dbService.getB(a);
});
CompletionStage<C> cFuture = aFuture.thenApplyAsync(a -> {
    return dbService.getC(a);
});
CompletionStage<D> dFuture = aFuture.thenApplyAsync(a -> {
    return dbService.getD(a);
});

CompletionStage<Something> sFuture = bFuture.thenCompose(b -> {
    cFuture.thenCombine(dFuture, (c, d) -> {
        // do something with b, c, d
        return new Something();
    });
});
  

Чтобы распространить это на любое количество одновременных этапов, мне нравится использовать пакет CompletableFutures от Spotify следующим образом:

 CompletionStage<List<A>> aFuture = dbService.getA(request);

CompletionStage<B> bFuture = aFuture.thenApplyAsync(a -> {
    return dbService.getB(a);
});
CompletionStage<C> cFuture = aFuture.thenApplyAsync(a -> {
    return dbService.getC(a);
});
CompletionStage<D> dFuture = aFuture.thenApplyAsync(a -> {
    return dbService.getD(a);
});

CompletionStage<Something> sFuture = CompletableFutures.combine((bFuture, cFuture, dFuture) -> {
    // do something with b, c, d
    return new Something();
}