简体   繁体   中英

Is this a CompletableFuture chaining anti-pattern?

Lately I have been applying a pattern where I chain futures together where for some future I don't care about the result but I do care it completed without exception before continuing the futures chain.

What I do is using thenCompose() and then supply it with a CompletableFuture that always ends with .thenApply(a) . Example:

futureR
   .thenCompose(r -> sequentialFutureTask1(r).thenApply(ignored -> r))
   .thenCompose(r -> sequentialFutureTask2(r).thenApply(ignored -> r))
   .thenCompose(r -> sequentialFutureTask3(r).thenApply(ignored -> r))

I've even streamlined this with a helper method:

futureFoo
   .thenCompose(identityComposing(r -> sequentialFutureTask1(r)))
   .thenCompose(identityComposing(r -> sequentialFutureTask2(r)))
   .thenCompose(identityComposing(r -> sequentialFutureTask3(r)))



public static <T> Function<T, CompletableFuture<T>> identityComposing(Function<T, CompletableFuture<?>> c) {
    return r -> cf.apply(r).thenApply(ignored -> r);
}

Is this an anti-pattern? It looks concise enough to me, but somehow I feel I missed some point.

Outside of the invisible side-effects pointed out by Antoniossss in the comments, which drives this a bit away from functional programming, the other issue is that you are still repeating yourself with all these identityComposing() wrapping calls.

As you said, the requirement is that those methods are called in sequence, so semantically you don't really need the "composition" result. The intent is really to chain the calls themselves.

This would be best represented by composing those calls directly instead:

futureR.thenAccept(r -> sequentialFutureTask1(r)
           .thenCompose(ignored -> sequentialFutureTask2(r))
           .thenCompose(ignored -> sequentialFutureTask3(r)));

Of course, there is still a lot of repetition here with the thenCompose(ignored -> …(r)) . If this is a frequent pattern you have, you could wrap this in the following method:

@SafeVarargs
public final <R> void executeInSequence(R r,
        Function<? super R, ? extends CompletionStage<?>> firstTask,
        Function<? super R, ? extends CompletionStage<?>>... otherTasks) {
    CompletionStage<?> prev = firstTask.apply(r);
    for (Function<? super R, ? extends CompletionStage<?>> task : otherTasks) {
        prev = prev.thenCompose(ignored -> task.apply(r));
    }
}

(I also wanted to do this with Stream but I didn't find a nice, clean solution)

which you would use as:

futureR.thenAccept(r -> executeInSequence(r,
        this::sequentialFutureTask1,
        this::sequentialFutureTask2,
        this::sequentialFutureTask3));

or if you prefer to pass futureR as parameter:

@SafeVarargs
public final <R> void executeAfter(CompletionStage<R> futureR,
        Function<? super R, ? extends CompletionStage<?>> firstTask,
        Function<? super R, ? extends CompletionStage<?>>... otherTasks) {
    futureR.thenAccept(r -> {
        CompletionStage<?> prev = firstTask.apply(r);
        for (Function<? super R, ? extends CompletionStage<?>> task : otherTasks) {
            prev = prev.thenCompose(ignored -> task.apply(r));
        }
    });
}

used as:

executeAfter(futureR,
        this::sequentialFutureTask1,
        this::sequentialFutureTask2,
        this::sequentialFutureTask3);

Alternatively, this executeAfter() can be implemented with one less parameter:

@SafeVarargs
public final <R> void executeAfter2(CompletionStage<R> futureR,
        Function<? super R, ? extends CompletionStage<?>>... otherTasks) {
    futureR.thenAccept(r -> {
        CompletionStage<?> prev = futureR;
        for (Function<? super R, ? extends CompletionStage<?>> task : otherTasks) {
            prev = prev.thenCompose(ignored -> task.apply(r));
        }
    });
}

but this prev = futureR causes an additional, useless thenCompose() that might be confusing (performance impact should be negligible since futureR is already completed when this will be invoked).

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