Future

Futures provide a higher-level abstraction that simplifies asynchronous programming very similar to Promise in Javascript.

Future is usually used with Executor:

import java.util.concurrent.Future;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutionException;

public class Main {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newFixedThreadPool(1);
        
        Future<Integer> future = executor.submit(() -> {
            Thread.sleep(1000);
            return 1;
        });
        
        System.out.println(future.get());
        executor.shutdown();
    }
}

It's also possible to create Future mannualy:

import java.util.concurrent.FutureTask;
import java.util.concurrent.ExecutionException;

public class Main {
    public static void main(String[] args) throws InterruptedException, ExecutionException {

        FutureTask<Integer> futureTask = new FutureTask<>(() -> {
            Thread.sleep(1000);
            return 1;
        });

        new Thread(futureTask).start();

        System.out.println(futureTask.get());
    }
}

CompletableFuture

CompletableFuture provides a more composable and flexible way to chain and combine Futures. You can use methods like thenApply, thenCombine, etc., to chain operations.

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            return 1;
        });

        CompletableFuture<Integer> result = future.thenApply((num) -> {
            return num * 2;
        });

        System.out.println(result.get());
    }
}

Handling exception

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
            if (Math.random() > 0.5) {
                throw new RuntimeException("Boom!");
            }
            return 1;
        });

        future
            .exceptionally(e -> {
                System.out.println(e.getMessage());
                return 0;
            })
            .thenAccept(result -> System.out.println("result: " + result));
    }
}