We use Futures in vertx in examples like:
Future fetchVehicle = getUserBookedVehicle(routingContext, client);
fetchVehicle.compose
If you want to feed the response from the previous request to the next request, and suppose you have different handlers for each response. You can add a helper method
private <T> Future<T> chain(Future<T> init, List<Function<T, Future<T>>> handlers) {
Future<T> result = init;
for (Function<T, Future<T>> handler : handlers) {
result = result.compose(handler);
}
return result;
}
And then change your code like this
Future<JsonObject> fetchVehicle = getUserBookedVehicle(routingContext, client);
Function<JsonObject, Future<JsonObject>> vehicleResponseHandler = vehicleJson ->
vehicleDoor(routingContext, client, vehicleJson, lock);
Function<JsonObject, Future<JsonObject>> anotherTrivialHandler = someJsonObj -> {
// add here new request by using information from someJsonObj
LOG.info("Hello from trivial handler {} ", someJsonObj);
return Future.succeededFuture(someJsonObj);
};
List<Function<JsonObject, Future<JsonObject>>> handlers = new ArrayList<>();
handlers.add(vehicleResponseHandler);
handlers.add(anotherTrivialHandler);
chain(fetchVehicle, handlers).setHandler( asyncResult -> {
if (asyncResult.succeeded()) {
handler.handle(Future.succeededFuture(new AsyncReply(200, "OK")));
} else {
handler.handle(Future.failedFuture(asyncResult.cause()));
}
});
But there is a limitation for this implementation which requires each chained Future
must have the same type parameter T
.
Here is a solution using map & reduce
that executes a method in an orderly fashion and returns the accumulated result in the form of a Future<String>
public static <T> Future<String> chainCall(List<T> list, Function<T, Future<String>> method){
return list.stream().reduce(Future.succeededFuture(),// the initial "future"
(acc, item) -> acc.compose(v -> method.apply(item)), // we return the compose of the previous "future" with "future" returned by next item processing
(a,b) -> Future.future()); // not used! only useful for parallel stream.
}
can be used as in the example below:
chainCall(conversation.getRequestList(), this::sendApiRequestViaBus);
where sendApiRequestViaBus
is:
/**
* @param request The request to process
* @return The result of the request processing.
*/
Future<String> sendApiRequestViaBus(ApiRequest request) {
Future<String> future = Future.future();
String address = CommandUtilsFactory.getInstance(request.getImplementation()).getApiClientAddress();
log.debug("Chain call start msgId {}", request.getId());
vertx.eventBus().send(address, JsonObject.mapFrom(request), deliveryOptions, res -> {
log.debug("Chain call returns {}", request.getId());
if (res.succeeded()) {
future.complete("OK");
} else {
future.fail("KO");
}
});
return future;
}
I hope it helps.
Here's something handy. Hope it helps.
public static <R> Future<List<R>> allOfFutures(List<Future<R>> futures) {
return CompositeFutureImpl.all(futures.toArray(new Future[futures.size()]))
.map(v -> futures.stream()
.map(Future::result)
.collect(Collectors.toList())
);
}