All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.sphere.sdk.meta.AsyncDocumentation Maven / Gradle / Ivy

There is a newer version: 1.0.0-M26
Show newest version
package io.sphere.sdk.meta;

import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
import java.util.concurrent.ForkJoinPool;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 

 

Why asynchronous

If you don't care about threads and asynchronous computation you will probably have a slow and inefficient application.

Suppose you want to show a customer detail page with the cart items and the customer data. For doing this, you need to fetch the cart and the customer. Let's suppose fetching those two unrelated documents from SPHERE.IO takes 100ms for each document.

{@include.example io.sphere.sdk.meta.AsyncDocumentationTest#serialWayToFetchCustomerAndCart()}

So it takes around 200ms since the requests are done one after another. By fetching them in parallel 100ms of time can be saved.

{@include.example io.sphere.sdk.meta.AsyncDocumentationTest#parallelWayToFetchCustomerAndCart()}

Using futures (We use it here as synonym for {@link java.util.concurrent.CompletableFuture} and {@link java.util.concurrent.CompletionStage}.) can be very handy for executing code in parallel.

You can use future APIs to run code in separate Threads so that the result will not be immediately available, but in the future. The overhead of creating a future can be lower than the overhead of creating new Thread.

Functional Composition

Functional composition covers transforming one future into another for the happy cases. {@link java.util.concurrent.CompletionStage#thenApply(java.util.function.Function)} and {@link java.util.concurrent.CompletionStage#thenCompose(java.util.function.Function)} will only be called if the future finishes successfully.

ThenApply (map, function returns directly a value)

Mostly, it is easier to reason about side-effect free code. A future is monad so you do not work with the value directly, but you provide functions to transform the value or the exception into a new future. To use a future for further computation, you probably need to use {@link java.util.concurrent.CompletionStage#thenApply(java.util.function.Function)}. {@include.example io.sphere.sdk.meta.AsyncDocumentationTest#thenApplyFirstDemo()} With {@link java.util.concurrent.CompletionStage#thenApply(java.util.function.Function)} you apply a function to a stage if this stage completes successfully. The function is a first class member, so you can store it in a value or even make it the return type of a method. {@include.example io.sphere.sdk.meta.AsyncDocumentationTest#thenApplyFirstDemoVerbose()} {@include.example io.sphere.sdk.meta.FunctionAsReturnValueDemo} It has similar semantics like {@link java.util.stream.Stream#map(java.util.function.Function)}. {@include.example io.sphere.sdk.meta.AsyncDocumentationTest#functionalCompositionMapStreamExample()}

ThenCompose (flatMap, function returns a CompletionStage)

Sometimes you run in situations where you create a new future inside a future. For example if you load a cart and want to fetch the first line item in it. {@include.example io.sphere.sdk.meta.AsyncDocumentationTest#shouldUseFlatMap()} Instead of creating an unmaintainable {@link java.util.concurrent.CompletionStage} of {@link java.util.concurrent.CompletionStage}, you can use {@link java.util.concurrent.CompletionStage#thenCompose(java.util.function.Function)}. {@include.example io.sphere.sdk.meta.AsyncDocumentationTest#flatMapFirstDemo()} It has similar semantics like {@link java.util.stream.Stream#flatMap(java.util.function.Function)}. {@include.example io.sphere.sdk.meta.AsyncDocumentationTest#functionalCompositionFlatMapStreamExample()}

Callbacks

For some occasions you do not want to transform a value, but to perform a side effect task, like logging a value or an error, writing sth. into a file or sending a response for a request. {@include.example io.sphere.sdk.meta.AsyncDocumentationCallbackTest#loggingCallbackExample()} {@link java.util.concurrent.CompletionStage#whenComplete(java.util.function.BiConsumer)} keeps the result as it is and performs side-effects, so it is nice to log in between and then map the stage to a new one: {@include.example io.sphere.sdk.meta.AsyncDocumentationCallbackTest#whenCompleteAsyncDemo()}

Creation and filling

Creation of a successful future

A future can be created as fulfilled immediately: {@include.example io.sphere.sdk.meta.AsyncDocumentationTest#createImmediatelyFulfilledFuture()} Also future can be fulfilled later: {@include.example io.sphere.sdk.meta.AsyncDocumentationTest#createFulfilledFuture()} For the immediately fulfilled future an SDK utility method also exists: {@include.example io.sphere.sdk.meta.AsyncDocumentationTest#createImmediatelyFulfilledFutureShortcut()}

Creation of a failed future

{@include.example io.sphere.sdk.meta.AsyncDocumentationTest#createFailedFuture()}

Using an SDK shortcut:

{@include.example io.sphere.sdk.meta.AsyncDocumentationTest#createFailedFutureShortcut()}

If you complete a future, it is possible that the same Thread is used for functional compositions or executing callbacks. If you don't want this, the calls need to use the methods which end with "Async".

Blocking Access and Immediate Access

{@link java.util.concurrent.CompletionStage} does not provide immediate or blocking access to its value or error, but it is possible, but not encouraged to transform the {@link java.util.concurrent.CompletionStage} with {@link CompletionStage#toCompletableFuture()} to a {@link java.util.concurrent.CompletableFuture}.

Blocking access

{@include.example io.sphere.sdk.meta.AsyncDocumentationTest#futureJoinDemo()}

Access with timeout

Future completes in time:

{@include.example io.sphere.sdk.meta.AsyncDocumentationTest#futureGetTimeoutDemo()}

future does not complete in time:

{@include.example io.sphere.sdk.meta.AsyncDocumentationTest#futureGetTimeoutDemoWithActualTimeout()}

Access for the impatient

Future completed:

{@include.example io.sphere.sdk.meta.AsyncDocumentationTest#demoGetNowCompleted()}

Future did not yet complete:

{@include.example io.sphere.sdk.meta.AsyncDocumentationTest#demoGetNow()}

Workaround if the value should be lazy computed:

{@include.example io.sphere.sdk.meta.AsyncDocumentationTest#testOrElseGet()} See {@link io.sphere.sdk.utils.CompletableFutureUtils#orElseGet(CompletionStage, Supplier)}.

Workaround if exception should be thrown:

{@include.example io.sphere.sdk.meta.AsyncDocumentationTest#testOrElseThrow()} {@include.example io.sphere.sdk.meta.AsyncDocumentationTest#testOrElseThrowHappyPath()} See {@link io.sphere.sdk.utils.CompletableFutureUtils#orElseThrow(CompletionStage, Supplier)} .

Summary of Blocking Access and Immediate Access methods

Blocking Access and Immediate Access methods of {@link java.util.concurrent.CompletableFuture}
 

future.join()

future.get()

future.get(12, TimeUnit.MILLISECONDS)

future.getNow("default")

returns value if present

x

x

x

x

blocks potentially forever

x

x

   

uses alternative, if value not present

     

x

throws TimeoutException

   

x

 

throws CompletionException

x

   

x

throws ExecutionException

 

x

x

 

throws only unchecked Exceptions

x

 

 

x

Java Functions

Since Java 8, the JDK provides Lambda Expressions and Method References.

Important {@link FunctionalInterface}s
  number of arguments behavior checked Exception purpose
{@link java.util.function.Function}<T,R> 1 create value   transforms one value into another
{@link java.util.function.BiFunction}<T,U,R> 2 create value   transforms two values into another
{@link java.util.function.Consumer}<T> 1 side-effects   side effect for one value
{@link java.util.function.BiConsumer}<T,U> 2 side-effects   side effect for two values
{@link java.util.function.Supplier}<T> 0 create value   on-demand creation of a value
{@link java.util.concurrent.Callable}<V> 0 create value x like Supplier but throws Exception
{@link java.lang.Runnable} 0 side-effects   task which causes side-effects

Threads and the Trinity

Which Thread is used for functional composition and callbacks depends on the method. For {@link java.util.concurrent.CompletionStage#thenApply(Function)}, {@link java.util.concurrent.CompletionStage#thenAccept(Consumer)} , {@link java.util.concurrent.CompletionStage#handle(BiFunction)} etc. exist three variants:

  1. {@link java.util.concurrent.CompletionStage#thenApply(Function)}, no suffix, if the future is not yet completed, the the thread which calls {@link java.util.concurrent.CompletableFuture#complete(Object)} is used to apply the function, if the future is completed, the thread which calls {@link java.util.concurrent.CompletionStage#thenApply(Function)} is used. So this method is discouraged if you use actors or tend to block threads.
  2. {@link java.util.concurrent.CompletionStage#thenApplyAsync(Function)} with suffix "Async" calls the function inside a Thread of {@link ForkJoinPool#commonPool()}. So you are better protected against deadlocks.
  3. {@link java.util.concurrent.CompletionStage#thenApplyAsync(Function, Executor)} with suffix "Async" and additional {@link Executor} parameter calls the function inside a Thread pool you specify as second parameter.

Error Handling

If an exception occurs with the computation, it should be propagated to the future with {@link java.util.concurrent.CompletableFuture#completeExceptionally(Throwable)}, but only once in the lifetime of the future. As a result the following try catch block does not make sense, since the error is inside the future which is most likely computed in another Thread:

{@include.example io.sphere.sdk.meta.AsyncDocumentationTest#wrongWayOfErrorHandling()}

Recover from a failure without a new CompletionStage

{@include.example io.sphere.sdk.meta.AsyncDocumentationTest#simpleRecover()}

Recover from a failure with producing a new CompletionStage

{@include.example io.sphere.sdk.meta.AsyncDocumentationTest#simpleRecover()}

Handle

{@link CompletionStage#exceptionally(Function)} is like applying {@link CompletionStage#thenApply(Function)} and then {@link CompletionStage#exceptionally(Function)}: {@include.example io.sphere.sdk.meta.AsyncDocumentationTest#handleLikeExceptionallyAndThenApply()}

You can use the exceptions to give error specific text to the user:

{@include.example io.sphere.sdk.meta.AsyncDocumentationTest#exceptionallyWithExceptionTypes()}

But you do not need to cover all problems:

{@include.example io.sphere.sdk.meta.AsyncDocumentationTest#exceptionallyWithExceptionTypesButUncoveredPart()}

Working with multiple futures

Traps

Advanced Examples

Summary

{@link java.util.concurrent.CompletionStage} vs. {@link java.util.concurrent.CompletableFuture}
  CompletionStage CompletableFuture
type interface concrete class
implements CompletionStage x x
functional composition x x
implements Future interface   x
can be filled with value or exception   x
can be cancelled   x
can check if completed   x
blocking usage directly possible   x
provides static methods for creation   x


CompletionStage methods
  value effect Function param Consumer param Runnable param maps value maps error or and Scala Future Play F.Promise
thenApply x   x     x       map map
thenCompose x   x     x       flatMap flatMap
thenAccept   x   x   x       onSuccess onSuccess
thenRun   x     x            
                       
exceptionally x   x       x     recover recover
handle x   x     x x     andThen  
whenComplete x x   x   x x        
                       
acceptEither   x   x   x   x      
thenAcceptBoth   x   x   x     x zip zip
applyToEither x   x     x   x   fallbackTo fallbackTo
thenCombine x   x     x     x    
runAfterEither   x     x     x      
runAfterBoth   x     x       x    

Further Topics

Further read and sources

*/ public final class AsyncDocumentation { private AsyncDocumentation() { } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy