What is the execution order of several thenAccept blocks for CompletedFuture

So, I have a method that returns CompletableFuture . Before returning, this method adds a block with thenAccept , which is executed after the completion of the CompletableFuture .

The caller of this method also adds another block with thenAccept . Obviously, this can continue with several chain calls.

In what order are the CompletionStage returned by the thenAccept calls being thenAccept ? Is the order of their addition guaranteed? If not, how can you guarantee that they are executed in the order in which they are added?

PS: I ask this from my own experience with CompletableFuture and this article

+5
source share
1 answer

You model the dependencies of the completion stages by linking them. If you associate two actions A and B with another action C , you define the relations A → C and B → C , but have no relationship between A and B and therefore there is no relation, in other words, you cannot even assume that one will work after another, i.e.

 CompletableFuture<String> base=CompletableFuture.supplyAsync(() -> { LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2)); return "source"; }); base.thenAccept(s -> { System.out.println("entered first consumer in "+Thread.currentThread()); LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); System.out.println("leaving first consumer"); }); LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2)); base.thenAccept(s -> { System.out.println("entered second consumer in "+Thread.currentThread()); LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); System.out.println("leaving second consumer"); }); 

most likely will print something like

 entered first consumer in Thread[ForkJoinPool.commonPool-worker-1,5,main] entered second consumer in Thread[main,5,main] leaving second consumer leaving first consumer 

Although, of course, there is no guarantee about this.


To ensure dependency between two consumers, you must appropriately link them, for example.

 CompletableFuture<String> base=CompletableFuture.supplyAsync(() -> { LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2)); return "source"; }); CompletableFuture<Void> next = base.thenAccept(s -> { System.out.println("entered first consumer in "+Thread.currentThread()); LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); System.out.println("leaving first consumer"); }); LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(2)); base.thenAcceptBoth(next, (s,ignored) -> { System.out.println("entered second consumer in "+Thread.currentThread()); LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1)); System.out.println("leaving second consumer"); }).join(); 

Here, the second consumer is tied to base and next to get the result from base , but depends on the completion of next s (which you would normally not require if there was no result for the transfer - perhaps you want to rethink your design if you have such a scenario) .

Alternatively, you can convert the first Consumer to Function that passes through this value, so you can bind it with thenApply to bind another thenAccept stage to thenAccept .

+6
source

All Articles