У меня есть следующий код с несколькими асинхронными вызовами, зависящими друг от друга (например, вызовы могут быть apis REST) и, в конце концов, обработать все результаты. Это мой пример кода:
private void foo1(String uuid, Handler<AsyncResult<JsonObject>> aHandler) {
//call json api, for example
JsonObject foo1 = new JsonObject();
foo1.put("uuid", "foo1");
aHandler.handle(Future.succeededFuture(foo1));
}
private void foo2(String uuid, Handler<AsyncResult<JsonObject>> aHandler) {
//call json api, for example
JsonObject foo2 = new JsonObject();
foo2.put("uuid", "foo2");
aHandler.handle(Future.succeededFuture(foo2));
}
private void foo3(String uuid, Handler<AsyncResult<JsonObject>> aHandler) {
//call json api, for example
JsonObject foo3 = new JsonObject();
foo3.put("uuid", "foo3");
aHandler.handle(Future.succeededFuture(foo3));
}
private void doSomething(JsonObject result1, JsonObject result2, JsonObject result3, Handler<AsyncResult<JsonObject>> aHandler) {
JsonObject finalResult =new JsonObject();
aHandler.handle(Future.succeededFuture(finalResult));
}
private void processToRefactor (String uuid, Handler<AsyncResult<JsonObject>> aHandler) {
foo1(uuid, ar -> {
if (ar.succeeded()) {
JsonObject foo1 = ar.result();
foo2(foo1.getString("uuid"), ar2 ->{
if (ar2.succeeded()) {
JsonObject foo2 = ar2.result();
foo3(foo2.getString("uuid"), ar3 -> {
if (ar3.succeeded()) {
JsonObject foo3 = ar3.result();
doSomething(foo1, foo2, foo3, aHandler);
} else {
ar3.cause().printStackTrace();
}
});
} else {
ar2.cause().printStackTrace();
}
});
} else {
ar.cause().printStackTrace();
}
});
}
В предыдущем коде у меня есть все результаты, доступные для использования в методе «doSomething», если все вызовы были успешными. Я попытался реорганизовать этот код, используя простой пример «HelloWord» по следующей ссылке https://streamdata.io/blog/vert-x-and-the-async-calls-chain/
Это мой результат:
private void process(String uuid, Handler<AsyncResult<JsonObject>> aHandler) {
Future<JsonObject> future = Future.future();
future.setHandler(aHandler);
Future<JsonObject> futureFoo1 = Future.future();
foo1(uuid, futureFoo1);
futureFoo1.compose(resultFoo1 -> {
Future<JsonObject> futureFoo2 = Future.future();
foo2(resultFoo1.getString("uuid"), futureFoo2);
return futureFoo2;
}).compose(resultFoo2 ->{
Future<JsonObject> futureFoo3 = Future.future();
foo3(resultFoo2.getString("uuid"), futureFoo3);
return futureFoo3;
}).compose(resultFoo3 -> {
// How to get result1, result2 and result3?
// doSomething(resultFoo1, resultFoo2, resultFoo3, aHandler);
}, future);
}
Новый код чище и понятнее, но при использовании compose в момент вызова функции «doSomething» у меня нет всех доступных результатов вызовов. Как мне получить все результаты в конце цепочки?
С другой стороны, как поступить, если один из методов вызова API возвращает массив? То есть для каждого элемента массива применяется цепочка функций, независимо от того, что у одних есть результаты, а у других нет. Например:
private void foo1Array(String uuid, Handler<AsyncResult<JsonArray>> aHandler) {
//call json api that return array, for example
JsonArray result = new JsonArray();
JsonObject foo1 = new JsonObject();
foo1.put("uuid", "foo1");
JsonObject foo2 = new JsonObject();
foo1.put("uuid", "foo2");
JsonObject foo3 = new JsonObject();
foo1.put("uuid", "foo3");
result.add(foo1);
result.add(foo2);
result.add(foo3);
aHandler.handle(Future.succeededFuture(result));
}
private void processArray(String uuid, Handler<AsyncResult<JsonObject>> aHandler) {
Future<JsonObject> future = Future.future();
future.setHandler(aHandler);
Future<JsonArray> futureFoo1 = Future.future();
foo1Array(uuid, futureFoo1);
futureFoo1.compose(resultArray -> {
List<Future> futures = new ArrayList<Future>();
for (int i = 0; i < resultArray.size(); i ++) {
JsonObject resultFoo1 = resultArray.getJsonObject(i);
Future<JsonObject> futureFoo2 = Future.future();
foo2(resultFoo1.getString("uuid"), aHandler);
futures.add(futureFoo2);
}
CompositeFuture.any(futures).setHandler(ar -> {
//What to do here?
});
}, future);
}
Как вызвать функции foo2, foo3, ... с результатом foo1Array, а затем использовать его в doSomething?




Если вы хотите более простой и без адского кода обратного вызова, переключитесь на rx, ваш приведенный выше код превратится во что-то вроде этого:
private Single<JsonObject> foo1(String uuid) {
JsonObject foo1 = new JsonObject();
foo1.put("uuid", "foo1");
return Single.just(foo1);
}
private Single<JsonObject> foo2(String uuid) {
JsonObject foo2 = new JsonObject();
foo2.put("uuid", "foo2");
return Single.just(foo2);
}
private Single<JsonObject> foo3(String uuid) {
JsonObject foo3 = new JsonObject();
foo3.put("uuid", "foo3");
return Single.just(foo3);
}
private Single<JsonObject> doSomething(String uuid) {
JsonObject finalResult =new JsonObject();
return Single.zip(foo1(uuid), foo2(uuid), foo3(uuid))
.map(results -> {
// Map zip results to finalResult
return finalResult;
});
}
private void processToRefactor (String uuid, Handler<AsyncResult<JsonObject>> aHandler) {
doSomething(uuid).subscribe();
}
Ваш первоначальный подход на самом деле не так уж и плох.
Чтобы улучшить код для лучшей «компоновки», вы должны изменить входной аргумент обработчика каждого метода fooX на что-то, что расширяет Handler<AsyncResult<JsonObject>> (например, Future) и в результате возвращает тот же обработчик, чтобы его можно было лучше использовать в `Future. compose, потому что переданный обработчик может использоваться как возвращаемое значение для каждого compose:
private <T extends Handler<AsyncResult<JsonObject>>> T foo1(String uuid, T aHandler) {
JsonObject foo1 = new JsonObject().put("uuid", "foo1");
aHandler.handle(Future.succeededFuture(foo1));
return aHandler; //<-- return the handler here
}
Во-вторых, чтобы получить доступ ко всем трем результатам на заключительном этапе, вы должны объявить три фьючерса вне цепочки. Теперь вы можете легко связать фьючерсы, используя вывод каждого метода foo в качестве результата для каждой композиции.
Future<JsonObject> futureFoo1 = Future.future();
Future<JsonObject> futureFoo2 = Future.future();
Future<JsonObject> futureFoo3 = Future.future();
foo1(uuid, futureFoo1).compose(resultFoo1 -> foo2(resultFoo1.getString("uuid"), futureFoo2))
.compose(resultFoo2 -> foo3(resultFoo2.getString("uuid"), futureFoo3))
.compose(resultFoo3 -> doSomething(futureFoo1.result(), //access results from 1st call
futureFoo2.result(), //access results from 2nd call
resultFoo3,
Future.<JsonObject>future().setHandler(aHandler))); //pass the final result to the original handler
Если вы не можете жить с «примесью» этого подхода (определение фьючерсов вне цепочки и изменение их внутри функции), вы должны передать исходные входные значения для каждого метода (= результат предыдущего вызова) вместе с результат, но я сомневаюсь, что это сделало бы код более читабельным.
Чтобы изменить тип в одном методе компоновки, метод fooX должен сделать преобразование, возвращающее не исходный обработчик, а новое будущее с другим типом
private Future<JsonArray> foo2(String uuid, Handler<AsyncResult<JsonObject>> aHandler) {
JsonObject foo2 = new JsonObject();
foo2.put("uuid", "foo2" + uuid);
aHandler.handle(Future.succeededFuture(foo2));
JsonArray arr = new JsonArray().add("123").add("456").add("789");
return Future.succeededFuture(arr);
}
спасибо, первое решение кажется наиболее подходящим и работает для меня.