简体   繁体   中英

Complete CompletableFuture with another CompletableFuture result

I'm doing async http call such way

public CompletableFuture<String> doPost(String path, String json) {
        CompletableFuture<String> result = new CompletableFuture<>();
        Request request = new Request.Builder().url(this.address + path).post(RequestBody.create(json, JSON)).build();
        httpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull IOException e) {
                result.completeExceptionally(new TerminationException());
            }

            @Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                result.complete(response.body().string());
            }
        });
    }

But it is possible that response will have one of the codes that I'll need to retry and the code should be

@Override
            public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                if (!retries.contains(responce.code()) {
                    result.complete(response.body().string());
                } else {
                    // Do retry here
                }
            }

In retry I want to call doPost recursively and use it's return value as a result of initial call. So it returns some completable future, how complete initial CF with it's result in async way (without doint.get())?

Thanks.

You can use delegation, eg

public CompletableFuture<String> doPost(String path, String json) {
    CompletableFuture<String> result = new CompletableFuture<>();
    doPostImpl(this.address + path, json, result, 10);
    return result;
}

private void doPostImpl(
    String url, String json, CompletableFuture<String> result, int maxRetries) {

    Request request = new Request.Builder()
        .url(url).post(RequestBody.create(json, JSON)).build();

    httpClient.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(@NotNull Call call, @NotNull IOException e) {
            result.completeExceptionally(new TerminationException());
        }

        @Override
        public void onResponse(
            @NotNull Call call, @NotNull Response response) throws IOException {

            if(maxRetries <= 0 || !retries.contains(response.code())) {
                result.complete(response.body().string());
            } else {
                doPostImpl(url, json, result, maxRetries - 1);
            }
        }
    });
}

The front-end method delegates to a method receiving the target future. When retrying, the implementation method is invoked again with the same future. Therefore, there is no need to transfer results from one future to another.


Taking the question literally, you can transfer the result of a future to another using

future2.whenComplete((value,throwable) -> {
    if(throwable != null) future1.completeExceptionally(throwable);
    else future1.complete(value);
});

but this could create a dependency chain as long as the number of retries. As shown above, there is no need for that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM