# Bài 4: Kết hợp nhiều CompletableFuture

Trong bài viết này, chúng ta sẽ tìm hiểu cách kết hợp nhiều `CompletableFuture` để thực hiện các tác vụ đồng thời hoặc theo chuỗi. Chúng ta sẽ khám phá các phương thức quan trọng như `thenCompose`, `thenCombine`, `allOf`, và `anyOf`. Mỗi phương thức sẽ được giải thích chi tiết cùng với các ví dụ minh họa cụ thể.

#### thenCompose và thenCombine

##### thenCompose

Phương thức `thenCompose` được sử dụng để kết hợp hai `CompletableFuture` theo kiểu chuỗi, nơi `CompletableFuture` thứ hai phụ thuộc vào kết quả của `CompletableFuture` thứ nhất. Nó cho phép bạn tạo một pipeline của các tác vụ không đồng bộ.

###### Cú pháp:

```java
public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn)
```

###### Ví dụ:

```java
import java.util.concurrent.CompletableFuture;

public class ThenComposeExample {
    public static void main(String[] args) {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
            .thenCompose(result -> CompletableFuture.supplyAsync(() -> result + " World"));

        future.thenAccept(result -> {
            System.out.println("Result: " + result); // Result: Hello World
        });

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
```

Trong ví dụ này:

* `CompletableFuture.supplyAsync` tạo một tác vụ trả về chuỗi "Hello".
    
* `thenCompose` tạo một `CompletableFuture` mới dựa trên kết quả của tác vụ trước, thêm " World" vào chuỗi.
    
* `thenAccept` in ra kết quả cuối cùng.
    

##### thenCombine

Phương thức `thenCombine` được sử dụng để kết hợp kết quả của hai `CompletableFuture` độc lập và trả về kết quả cuối cùng. Nó nhận hai `CompletableFuture` và một `BiFunction` để kết hợp kết quả của chúng.

###### Cú pháp:

```java
public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn)
```

###### Ví dụ:

```java
import java.util.concurrent.CompletableFuture;

public class ThenCombineExample {
    public static void main(String[] args) {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 2);
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 3);

        CompletableFuture<Integer> resultFuture = future1.thenCombine(future2, (result1, result2) -> result1 + result2);

        resultFuture.thenAccept(result -> {
            System.out.println("Result: " + result); // Result: 5
        });

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
```

Trong ví dụ này:

* `CompletableFuture.supplyAsync` tạo hai tác vụ độc lập trả về các giá trị 2 và 3.
    
* `thenCombine` kết hợp kết quả của hai tác vụ, cộng hai giá trị lại với nhau.
    
* `thenAccept` in ra kết quả cuối cùng.
    

#### allOf và anyOf

##### allOf

Phương thức `allOf` được sử dụng để kết hợp nhiều `CompletableFuture` và tạo ra một `CompletableFuture` hoàn thành khi tất cả các `CompletableFuture` trong tập hợp hoàn thành. Nó không trả về kết quả của các `CompletableFuture` đã kết hợp mà chỉ báo hiệu rằng tất cả chúng đã hoàn thành.

###### Cú pháp:

```java
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
```

###### Ví dụ:

```java
import java.util.concurrent.CompletableFuture;

public class AllOfExample {
    public static void main(String[] args) {
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(1000);
                System.out.println("Task 1 completed");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(2000);
                System.out.println("Task 2 completed");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        CompletableFuture<Void> future3 = CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(3000);
                System.out.println("Task 3 completed");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2, future3);

        allFutures.thenRun(() -> System.out.println("All tasks completed"));

        try {
            allFutures.join();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```

Trong ví dụ này:

* Ba `CompletableFuture` độc lập thực hiện các tác vụ không đồng bộ với các thời gian chờ khác nhau.
    
* `CompletableFuture.allOf` kết hợp cả ba `CompletableFuture` và tạo một `CompletableFuture` hoàn thành khi tất cả các tác vụ hoàn thành.
    
* `thenRun` in ra "All tasks completed" khi tất cả các tác vụ hoàn thành.
    

##### anyOf

Phương thức `anyOf` được sử dụng để kết hợp nhiều `CompletableFuture` và tạo ra một `CompletableFuture` hoàn thành khi bất kỳ `CompletableFuture` nào trong tập hợp hoàn thành. Nó trả về kết quả của `CompletableFuture` hoàn thành đầu tiên.

###### Cú pháp:

```java
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs)
```

###### Ví dụ:

```java
import java.util.concurrent.CompletableFuture;

public class AnyOfExample {
    public static void main(String[] args) {
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(1000);
                System.out.println("Task 1 completed");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(2000);
                System.out.println("Task 2 completed");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        CompletableFuture<Void> future3 = CompletableFuture.runAsync(() -> {
            try {
                Thread.sleep(3000);
                System.out.println("Task 3 completed");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(future1, future2, future3);

        anyFuture.thenAccept(result -> System.out.println("First task completed"));

        try {
            anyFuture.join();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
```

Trong ví dụ này:

* Ba `CompletableFuture` độc lập thực hiện các tác vụ không đồng bộ với các thời gian chờ khác nhau.
    
* `CompletableFuture.anyOf` kết hợp cả ba `CompletableFuture` và tạo một `CompletableFuture` hoàn thành khi bất kỳ tác vụ nào hoàn thành trước.
    
* `thenAccept` in ra "First task completed" khi một trong các tác vụ hoàn thành.
    

### Kết Luận

Trong bài viết này, chúng ta đã tìm hiểu cách kết hợp nhiều `CompletableFuture` để thực hiện các tác vụ đồng thời hoặc theo chuỗi bằng cách sử dụng các phương thức `thenCompose`, `thenCombine`, `allOf`, và `anyOf`. Các phương thức này cho phép bạn xây dựng các pipeline tác vụ không đồng bộ phức tạp và quản lý kết quả của chúng một cách hiệu quả.

### Bài Tập Thực Hành

1. Sử dụng `thenCompose` để kết hợp hai `CompletableFuture` theo kiểu chuỗi, nơi tác vụ thứ hai phụ thuộc vào kết quả của tác vụ thứ nhất.
    
2. Sử dụng `thenCombine` để kết hợp kết quả của hai `CompletableFuture` độc lập và trả về kết quả cuối cùng.
    
3. Sử dụng `allOf` để kết hợp ba `CompletableFuture` và in ra thông báo khi tất cả các tác vụ hoàn thành.
    
4. Sử dụng `anyOf` để kết hợp ba `CompletableFuture` và in ra thông báo khi một trong các tác vụ hoàn thành trước.
    

### Lời Khuyên và Best Practices

* **Kết hợp tác vụ một cách hợp lý:** Sử dụng `thenCompose` và `thenCombine` để kết hợp các tác vụ một cách hợp lý, giúp tối ưu hóa luồng công việc và quản lý kết quả hiệu quả.
    
* **Theo dõi trạng thái các tác vụ:** Sử dụng `allOf` và `anyOf` để theo dõi trạng thái hoàn thành của nhiều tác vụ không đồng bộ, giúp bạn kiểm soát luồng công việc tốt hơn.
    
* **Xử lý ngoại lệ:** Đảm bảo rằng bạn bắt và xử lý ngoại lệ trong các tác vụ không đồng bộ để tránh các lỗi không mong muốn.
