返回 void 与返回 CompletionStage<Void> 之间有什么区别吗?

Is there any difference between returning a void vs returning a CompletionStage<Void>?

更具体地说,有什么区别,在这两段代码中,一个 returning void 和另一个 returning CompletionStage<Void>

代码示例 1:

public CompletionStage<Response> performTask(Request request) throws MyException {
    doValidation(request);
    return service.performTask(request).thenCompose(serviceResponse -> CompletableFuture.completedFuture(buildMyResponse(serviceResponse)));
}

private void doValidation(Request request) throws MyException {
    CompletableFuture.runAsync(() -> {
        // Validate
        if (*someCondition*) {
            throw new MyException(.....);
        }
    });
}

代码示例 2:

public CompletionStage<Response> performTask(Request request) throws MyException {
    return doValidation(request).thenCompose(x -> {
        return service.performTask(request).thenCompose(serviceResponse -> CompletableFuture.completedFuture(buildMyResponse(serviceResponse)));
    });
}

private CompletionStage<Void> doValidation(Request request) throws MyException {
    return CompletableFuture.runAsync(() -> {
        // Validate
        if (*someCondition*) {
            throw new MyException(.....);
        }
    });
}

我正在回答你的第一个问题

What advantages of using one of void or CompletionStage<Void> over the other?

要了解差异,您应该尝试记录流程。它将帮助您确定它们的工作方式有何不同。

这是一个与您采用的方法类似的示例。我想出了我自己的例子,因为我不知道你的代码。

public class Test {

    public static void main(String[] args) {
        tryWithVoid();
        //      tryWithCompletionStageVoid();
    }

    public static void tryWithVoid() {
        System.out.println("Started");
        validate(null);
        System.out.println("Exit");
    }

    public static void tryWithCompletionStageVoid() {
        System.out.println("Started");
        validateCS(null).thenRun(() -> System.out.println("Validation complete"));
        System.out.println("Exit");
    }

    public static CompletionStage<Void> validateCS(String val) {
        return CompletableFuture.runAsync(() -> {
            if (val == null) {
                System.out.println("Validation failed! value is null");
            }
        });
    }

    public static void validate(String val) {
        CompletableFuture.runAsync(() -> {
            if (val == null) {
                System.out.println("Validation failed! value is null");
            }
        });
    }
}

如果你 运行 tryWithVoid(),你会看到输出将是:

Started
Exit
Validation failed! value is null

然而,当您 运行 tryWithCompletionStageVoid() 时,它将是:

Started
Validation failed! value is null
Validation complete
Exit

在第一种方法中,tryWithVoid(),它不等待操作完成。因此操作的结果可能对调用后的代码可用,也可能不可用。

但是,在第二种方法 tryWithCompletionStageVoid() 中,它会等待该阶段完成,然后 运行 进入下一阶段。

如果阶段之间没有依赖关系,可以使用 void

不同之处在于,使用 returning a void 你不能在 CompletableFuture 完成后做任何链接到 CompletableFuture 的事情。

当您 return CompletableFuture<Void> 时,您可以将更多内容链接到它。例如调用 thenRun()thenRunAsync() 或只是 join() 以等待它在需要时完成。

所以这取决于调用者是否关心 CompletableFuture 的状态并希望应用附加到它的进一步逻辑。

在你的例子中,在示例 1 中,doValidation() 是 运行 在一个单独的线程上,无论它发生什么都无关紧要。 service.performTask() 将独立于它发生,甚至可以在 doValidation() 之前开始 and/or 完成。

在示例 2 中,doValidation() 必须在 performTask() 实际发生之前成功完成。所以看起来这个版本实际上是正确的。请记住 main 线程 运行 CompletionStage<Response> performTask() 将在设置管道后立即完成并且 return ,实际的 performValidation() 仍然可以运行(或者甚至可能还没有开始)。

顺便说一句,你可以简化调用如下:

service.performTask(request).thenApply(this::buildMyResponse);

您不必再次用 CompletableFuture 包装它,只需用 thenCompose() 再次打开它即可。