CompletionStage — 비동기 계산의 단계
CompletionStage — 비동기 계산의 단계
CompletionStage<T>는 **비동기 계산의 한 단계(stage)**예요. 다른 CompletionStage가 완료되면 액션을 수행하거나 값을 계산해요. 한 단계가 완료되면 다시 다른 의존 단계들을 촉발할 수 있어, 파이프라인처럼 연산을 이어 붙일 수 있어요. 대표 구현체는 CompletableFuture예요.
본문
기본 형태
이 인터페이스가 정의하는 기능은 몇 가지 기본 형태로, 다양한 사용 스타일을 담기 위한 더 큰 메서드 집합으로 확장돼요. 단계가 수행하는 계산은 인자와 결과를 요구하는지에 따라 Function(apply), Consumer(accept), Runnable(run)으로 표현돼요:
stage.thenApply(x -> square(x))
.thenAccept(x -> System.out.print(x))
.thenRun(() -> System.out.println());
추가 형태인 compose는 완료 스테이지를 반환하는 함수들로 계산 파이프라인을 구성할 수 있게 해줘요. 스테이지 계산에 넘어가는 인자는 촉발 단계(tiggering stage) 계산의 결과예요.
트리거 방식
한 단계의 실행은 단일 단계, 두 단계 모두, 또는 두 단계 중 하나의 완료로 촉발될 수 있어요.
- 단일 단계 의존:
then접두사 메서드 - 두 단계 모두 완료: 결과나 효과를 결합(
thenCombine,thenAcceptBoth,runAfterBoth) - 두 단계 중 하나 완료: 어느 쪽 결과/효과가 쓰일지 보장하지 않음(
applyToEither,acceptEither,runAfterEither)
실행 방식
단계 간 의존은 계산의 촉발을 제어하지만 특정 순서를 보장하진 않아요. 새 단계의 계산은 세 가지 방식으로 배치될 수 있어요:
- 기본 실행(default)
- 기본 비동기 실행(
async접미사 — 단계의 기본 비동기 실행 수단 사용) - 커스텀(제공된
Executor사용)
Executor 인자를 명시하는 메서드는 임의의 실행 속성을 가질 수 있고 동시 실행을 지원하지 않을 수도 있어요.
예외 처리
두 메서드 형태(handle, whenComplete)는 촉발 단계가 정상이든 예외적이든 무조건 계산을 지원해요. exceptionally는 촉발 단계가 예외적으로 완료될 때만 대체 결과를 계산하며, 자바 catch 키워드와 비슷해요.
그 외의 경우, 단계의 계산이 (비검사) 예외나 오류로 갑자기 끝나면, 그 완료를 요구하는 모든 의존 단계도 예외적으로 완료되고 그 예외를 cause로 가진 CompletionException이 전달돼요. 메서드 handle은 촉발 단계의 결과와 예외를 모두 받아 임의의 결과를 계산하는 가장 일반적인 연속 단계 생성 방법이에요. 두 메서드 모두 아래처럼 구조화된 계산을 가져야 해요:
function stage.complete -> (result, exception) -> {
if (exception == null) {
// triggering stage completed normally
} else {
// triggering stage completed exceptionally
}
}
참고: 완료 결과를 전달하는 데 쓰는 인자(타입 T 파라미터)는 null일 수 있지만, 다른 어떤 파라미터에 null을 넘기면 NullPointerException이 던져져요.
이 인터페이스는 단계를 처음 만들거나 강제 완료, 완료 상태 조회, 완료 대기를 위한 메서드는 정의하지 않아요. toCompletableFuture()가 공통 변환 타입을 제공해 서로 다른 구현 간 상호운용을 가능하게 해요.
then 계열 (단일 단계 의존)
<U> CompletionStage<U> thenApply(Function<? super T,? extends U> fn) — 이 단계가 정상 완료되면, 그 결과를 인자로 주어진 함수를 실행해 반환하는 새 CompletionStage를 반환해요. Optional.map/Stream.map과 유사해요. thenApplyAsync(fn)·thenApplyAsync(fn, executor) 변형이 있어요.
CompletionStage<Void> thenAccept(Consumer<? super T> action) — 정상 완료되면 결과를 인자로 주어진 액션을 실행해요. thenAcceptAsync(action)·thenAcceptAsync(action, executor) 변형이 있어요.
CompletionStage<Void> thenRun(Runnable action) — 정상 완료되면 주어진 액션을 실행해요(결과 사용 없음). thenRunAsync(action)·thenRunAsync(action, executor) 변형이 있어요.
thenCompose (파이프라인 구성)
<U> CompletionStage<U> thenCompose(Function<? super T,? extends CompletionStage<U>> fn) — 이 단계가 정상 완료되면 함수를 호출해 다른 CompletionStage를 얻고, 그 단계가 완료되면 같은 값으로 완료되는 새 단계를 반환해요. Optional.flatMap/Stream.flatMap과 유사해요. 진행을 보장하려면 함수가 결과의 최종 완료를 조정해야 해요. thenComposeAsync(fn)·thenComposeAsync(fn, executor) 변형이 있어요.
thenCombine / thenAcceptBoth / runAfterBoth (두 단계 모두)
<U,V> CompletionStage<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn) — 이 단계와 other가 모두 정상 완료되면 두 결과를 인자로 함수를 실행해요. thenCombineAsync 변형이 있어요.
<U> CompletionStage<Void> thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T,? super U> action) — 두 단계가 모두 완료되면 두 결과를 인자로 액션을 실행해요.
CompletionStage<Void> runAfterBoth(CompletionStage<?> other, Runnable action) — 두 단계가 모두 완료되면 액션을 실행해요(결과 사용 없음).
applyToEither / acceptEither / runAfterEither (두 단계 중 하나)
<U> CompletionStage<U> applyToEither(CompletionStage<? extends T> other, Function<? super T,U> fn) — 이 단계나 other 중 하나가 정상 완료되면, 해당하는 결과를 인자로 함수를 실행해요.
CompletionStage<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action) — 하나가 완료되면 해당 결과를 인자로 액션을 실행해요.
CompletionStage<Void> runAfterEither(CompletionStage<?> other, Runnable action) — 하나가 완료되면 액션을 실행해요.
handle / whenComplete (무조건 계산)
<U> CompletionStage<U> handle(BiFunction<? super T,Throwable,? extends U> fn) — 이 단계가 정상이든 예외적으로든 완료되면, 단계의 결과(null일 수 있음)와 예외(null일 수 있음)를 인자로 함수를 실행하고 함수의 결과로 반환 단계를 완료해요. handleAsync 변형이 있어요.
CompletionStage<T> whenComplete(BiConsumer<? super T,? super Throwable> action) — 이 단계와 같은 결과/예외를 가진 새 단계를 반환하되, 이 단계가 완료될 때 주어진 액션을 실행해요. handle과 달리 완료 결과를 변환하도록 설계되지 않았으므로 액션은 예외를 던지면 안 돼요.
exceptionally 계열 (예외 전용)
CompletionStage<T> exceptionally(Function<Throwable,? extends T> fn) — 이 단계가 예외적으로 완료되면, 예외를 인자로 함수를 실행해 반환하는 새 단계를 반환해요. 정상 완료되면 같은 값으로 정상 완료돼요. 자바 catch와 비슷해요.
CompletionStage<T> exceptionallyAsync(Function<Throwable,? extends T> fn) — 기본 비동기 실행 수단으로 예외 처리 함수를 실행해요. exceptionallyAsync(fn, executor) 변형이 있어요. (JDK 12+)
CompletionStage<T> exceptionallyCompose(Function<Throwable,? extends CompletionStage<T>> fn) — 예외적으로 완료되면 예외에 함수를 적용한 결과로 새 단계를 구성(compose) 해요. exceptionallyComposeAsync(fn)·exceptionallyComposeAsync(fn, executor) 변형이 있어요. (JDK 12+)
변환
CompletableFuture<T> toCompletableFuture() — 이 단계와 같은 완료 속성을 유지하는 CompletableFuture를 반환해요. 이미 CompletableFuture면 자기 자신을 반환할 수 있어요.