How to sequence a task to execute once all Single's in a collection complete

83 views Asked by At

I am using Helidon DBClient transactions and have found myself in a situation where I end up with a list of Singles, List<Single<T>> and want to perform the next task only after completing all of the singles.

I am looking for something of equivalent to CompletableFuture.allOf() but with Single.

I could map each of the single toCompletableFuture() and then do a CompletableFuture.allOf() on top, but is there a better way? Could someone point me in the right direction with this?

--

Why did I end up with a List<Single>?

I have a collection of POJOs which I turn into named insert .execute() all within an open transaction. Since I .stream() the original collection and perform inserts using the .map() operator, I end up with a List when I terminate the stream to collect a List. None of the inserts might have actually been executed. At this point, I want to wait until all of the Singles have been completed before I proceed to the next stage.

This is something I would naturally do with a CompletableFuture.allOf(), but I do not want to change the API dialect for just this and stick to Single/Multi.

1

There are 1 answers

1
Romain Grecourt On BEST ANSWER

Single.flatMap, Single.flatMapSingle, Multi.flatMap will effectively inline the future represented by the publisher passed as argument.

You can convert a List<Single<T>> to Single<List<T>> like this:

List<Single<Integer>> listOfSingle = List.of(Single.just(1), Single.just(2));
Single<List<Integer>> singleOfList = Multi.just(listOfSingle)
                                          .flatMap(Function.identity())
                                          .collectList();

Things can be tricky when you are dealing with Single<Void> as Void cannot be instantiated and null is not a valid value (i.e. Single.just(null) throws a NullPointerException).

// convert List<Single<Void>> to Single<List<Void>>
Single<List<Void>> listSingle =
        Multi.just(List.of(Single.<Void>empty(), Single.<Void>empty()))
             .flatMap(Function.identity())
             .collectList();

// convert Single<List<Void>> to Single<Void>
// Void cannot be instantiated, it needs to be casted from null
// BUT null is not a valid value...
Single<Void> single = listSingle.toOptionalSingle()
                                // convert Single<List<Void>> to Single<Optional<List<Void>>>
                                // then Use Optional.map to convert Optional<List<Void>> to Optional<Void>
                                .map(o -> o.map(i -> (Void) null))
                                // convert Single<Optional<Void>> to Single<Void>
                                .flatMapOptional(Function.identity());

// Make sure it works
single.forSingle(o -> System.out.println("ok"))
      .await();