简体   繁体   中英

How to access all previous CompletionStage results in CompletableFuture chain

I'm working in a Java 8 application. I have 3 methods that return CompletionStage:

  1. CompletionStage<Edition> editionService.loadById(editionId);
  2. CompletionStage<Event> eventService.loadById(eventId);
  3. CompletionStage<List<EditionDate>> editionDateService.loadByEditionId(editionId);

And a method that consolidates these values into a Result

  1. CompletionStage<Result> getResult(Edition edition, Event event, List<EditionDate> editionDates)

Methods 1 and 3 can be ran independently but calling method 2 depends on the result of method 1. And obviously method 4 depends on all of them to run. My question is, what's the best way to call these methods using CompletableFuture api; this is the best I can come up with but I'm not sure it's the best way to do it:

editionService.loadById(editionId)
  .thenCompose(edition -> eventService.loadById(edition.getEventId()))
  .thenCombine(editionDateService.loadByEditionId(editionId), 
              (event, editionDates) -> getResult(edition, event, editionDates) );

But this way I don't have access to my edition result so I'm in a little bit of a loss. Any method I should be using that I'm not taking into account?

You can write it as

CompletionStage<Result> result = editionService.loadById(editionId)
    .thenCompose(edition -> eventService.loadById(edition.getEventId())
        .thenCombine(editionDateService.loadByEditionId(editionId),
            (event, editionDates) -> getResult(edition, event, editionDates) ) )
    .thenCompose(Function.identity());

but then, editionDateService.loadByEditionId will be triggered only after editionService.loadById has been completed, which is an unnecessary dependency.

The simplest solution is not to try to write everything as a single expression:

CompletionStage<List<EditionDate>> datesStage=editionDateService.loadByEditionId(editionId);
CompletionStage<Result> result = editionService.loadById(editionId)
    .thenCompose(edition -> eventService.loadById(edition.getEventId())
        .thenCombine(datesStage, (event, dates) -> getResult(edition, event, dates)))
    .thenCompose(Function.identity());

easiest solution would be to have the edition available from inside the event. Or to have the call to 2 wrapped in anoher method that return a pair(edition, event) instead

Something like the following code looks good to me but not able to test it with only that piece of your code, so up to you to test it and make it cleaner. This is only a proof of concept :)

public static class Pair{
        public Edition edition;
        public Event event;

        public Pair(Edition edition, Event event) {
            this.edition = edition;
            this.event = event;
        }
    }

    public static CompletionStage<Pair> wrap(Edition edition){
        CompletionStage<Event> event = eventService.loadById(edition.getEventId());
        return event.thenApply(ev -> new Pair(edition, ev));        
    }


    public static void main(String[] args) {
        int editionId = 42;


        editionService.loadById(editionId)
                .thenCompose(edition -> wrap(edition))
                .thenCombine(editionDateService.loadByEditionId(editionId),
                        (wrapped, editionDates) -> getResult(wrapped.edition, wrapped.event, editionDates) );
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM