Вы можете использовать CompletableFuture
для реализации обещаний в Java. Проблема в том, что вы пытаетесь передать две разные вещи по «конвейеру»: запрос, который является изменчивым и (иногда) изменяющимся, и результат, который накапливается в ходе вызовов.
Я справился с этим, создав класс с именем Pipe
, у которого есть запрос и накопитель результатов. У него есть геттеры для обоих, и у него есть несколько удобных методов для возврата нового объекта с накопленными результатами или даже для изменения запроса и накопления в одном вызове. Это делает код API цепочки намного чище.
Методы with*
после полей, конструктора и геттеров - это те, которые обрабатывают накопление и мутацию. Метод chain
объединяет все это:
import java.util.concurrent.CompletableFuture;
public class Pipe {
private Request req;
private String out;
public Pipe(Request req, String out) {
this.req = req;
this.out = out;
}
public Request getReq() {
return req;
}
public String getOut() {
return out;
}
public Pipe with(String data) {
return new Pipe(req, out + data);
}
public Pipe withABC(String abc, String data) {
req.setABC(abc);
return new Pipe(req, out + data);
}
public Pipe withXYZ(String xyz, String data) {
req.setXYZ(xyz);
return new Pipe(req, out + data);
}
public static void chain(Request req) throws Exception {
var promise = CompletableFuture.supplyAsync(() -> new Pipe(req, ""))
.thenApply(pipe -> {
Response res = execute(pipe.getReq());
return pipe.withABC(res.getABC(), res.getOut());
})
.thenApply(pipe -> {
Response res = execute(pipe.getReq());
return pipe.withXYZ(res.getXYZ(), res.getOut());
})
.thenApply(pipe -> {
Response res = execute(pipe.getReq());
return pipe.with(res.getOut());
});
var result = promise.get().getOut();
System.out.println(result);
}
public static Response execute(Request req) {
return req.getResponse();
}
}
Поскольку он работает асинхронно, он может генерировать InterruptedException, а также может генерировать ExecutionException, если что-то еще прерывается. Я не знаю, как вы хотите справиться с этим, поэтому я просто объявил chain
бросить.
Если вы хотите применить n операций в цикле, вы должны продолжать переназначать обещание обратно, как показано ниже:
var promise = CompletableFuture.supplyAsync(() -> new Pipe(req, ""));
for (...) {
promise = promise.thenApply(pipe -> {
Response res = execute(pipe.getReq());
return pipe.with(res.getOut());
});
}
var result = promise.get().getOut();
Я использовал здесь вывод типа Java 10 с var
, но типы promise
и result
будут CompletableFuture<Pipe>
и String
соответственно.
(Примечание: может быть лучше сделать Request
неизменным и передать новый, измененный по конвейеру, а не мутировать его. С другой стороны, вы также можете обернуть StringBuilder
вместо String
и данные, которые вы накапливаете, также должны быть изменяемыми. Сейчас это странное сочетание изменчивости и неизменности, но это соответствует тому, что делал ваш код.)