外部APIAを呼び出し、その応答を使用してAPI Bのリクエストにフィードし、それを呼び出した後、APIAの呼び出し元に応答を返す状況があります。以下のようなもの
method(){
response = call API A
}
method_for_API_A(){
handler() ->{
API_B
}
return response;
}
method_for_API_B(){
//code to call API B
}
ここで私が直面しているのは、APIAメソッドがBからの応答を待たずに応答を返すことです。
Vert.xのexecuteBlockingメソッドについて確認し、「ブロッキングキュー」を使用しようとしましたが、意図したとおりに実行できませんでした。誰かが正しいやり方を教えてくれませんか。よろしくお願いします。
編集:正確なシナリオを説明するためだけに
Class MyClass{
public Response method_A (Request request){
String respFromApiA = Call_API_A(request) ; // STEP 1
Response respFromApiB = Call_API_B(request, respFromApiA); // STEP 2
Print(respFromApiB) // PRINT FINAL Response
return respFromApiB; // STEP 3
}
String Call_API_A(Request request){
// Implementation
Print(string); // PRINT API A response
return string
}
Response Call_API_B(Response response){
// Implementation
Print(response); // PRINT API B response
return response;
}
}
Javaでvert.xフレームワークを使用しています。実行中に発生するのは、フローがステップ1に到達し、APIA呼び出しを開始することです。ステップ2に進み(「respFromApiA」を待たずに)、API Bを呼び出します(respFromApiAがNULLであるため、最終的に失敗します)。そして最後に、フローはステップ3に進み、ここから戻ります。 (APIAおよびAPIBの結果を待たずに)。印刷注文を見ると、次のようになります
PRINT FINAL Response
PRINT API A response
PRINT API B response
私が達成しようとしていることは何ですか?
Wait for API A response.
Make call to API B. Wait for API B response.
Return response got from API B.
今回ははっきりさせたいと思います。さらに入力が必要な場合はお知らせください。
Vert.xは高度に非同期です。ほとんどの操作は実際にはすぐに返されますが、その結果は後の時点でHandler
が利用できるようになります。ここまでは順調ですね。私があなたを正しく理解しているなら、あなたがB
のHandler
でA
を呼び出す必要があるよりも。この場合、A
を終了する必要があり、B
を呼び出す前に、結果を利用できます。
callA(asyncResultA -> {
System.out.println("Result A: " + asyncResultA.result());
callB(asyncResultB -> {
System.out.println("Result B:" + asyncResultB.result());
});
});
しかし、あなたが試みているのは、何かを非同期同期にすることです。メインプログラムフローで非同期の結果を利用できるようにすることはできませんし、そうすべきではありません。それは機能しません。
String respFromApiA = Call_API_A(request); // STEP 1
Response respFromApiB = Call_API_B(request, respFromApiA); // STEP 2
Print(respFromApiB); // PRINT FINAL Response
return respFromApiB; // STEP 3
Call_API_A
は非同期で計算されるため、実際には結果を返すことができません。結果は、Call_API_A
のHandler
でのみ使用できます(上記の私の例を参照)。 Call_API_B
についても同じです。したがって、Call_API_B
の結果を返すことはできません。クラスの呼び出し元も、Handler
を使用してクラスを呼び出す必要があります。
今、いくつかの追加情報。あなたの場合、複数の非同期結果が互いに依存しているという問題があります。 Vert.xは、非同期結果を処理するためのはるかに便利な方法、いわゆるFutures
を提供します。 Future
(Promise
と呼ばれることもありますが、Javaの世界ではFuture
と呼ばれます)は、非同期呼び出しの結果のプレースホルダーです。 ドキュメント でそれらについて読んでください。
Future
を使用すると、次のようなことができます。
Future<...> callAFuture = Future.future();
callA(asyncResultA -> {
if (asyncResultA.succeeded()) {
System.out.println("A finished!");
callAFuture.complete(asyncResultA.result());
} else {
callAFuture.fail(asyncResultA.cause());
}
});
したがって、B
の非同期結果を同期的に返そうとする代わりに、Future
を返して、クラスの呼び出し先がA
とB
の両方の非同期結果に登録できるようにする必要があります。
これがお役に立てば幸いです。
編集:戻り値としての未来
callA
をラップして、Future
を操作できるようにするとします。あなたはこのようにそれを行うことができます:
public Future<String> doSomethingAsync() {
Future<String> callAFuture = Future.future();
// do the async stuff
callA(asyncResultA -> {
if (asyncResultA.succeeded()) {
System.out.println("A finished!");
callAFuture.complete(asyncResultA.result());
} else {
callAFuture.fail(asyncResultA.cause());
}
});
// return Future with the asyncResult of callA
return callAFuture;
}
この関数の呼び出し元は、次のようにFutureを使用できます。
Future<String> doSomethingFuture = doSomethingAsync();
doSomethingFuture.setHandler(somethingResult -> {
// ... doSomethingAsync finished
});
複数のFuture
を同時に作成したいが、それらが相互に依存していない場合は、それらを作成することもできます。
CompositeFuture.all(futureA, futureB).setHandler(connections -> {
// both Futures completed
});
Vert.xのような非同期環境で作業する場合、ほとんどの場合、soon-to-be-available-result別名Future
で作業します。また、Handler
のFuture
では、別の非同期呼び出しを行うことがよくあります。 Future
のFuture
のcallB
の例のように、callA
をHandler
でラップします。
Future
の非同期結果をHTTP応答としてどのように返しますか?このような:
router.route("/").handler(routingContext -> {
HttpServerResponse response = routingContext.response();
Future<String> future = doSomethingAsync();
future.setHandler(somethingResult -> {
if (somethingResult.succeeded()) {
response
.end(somethingResult.result());
} else {
routingContext.fail(500);
}
});
});
Future
を使用してresults
を返し、他のメソッドで再び使用しました。これが私の実装です。誰かに役立つことを願っています。
public static void ussdMessages(RoutingContext routingContext){
String codeService = routingContext.getBodyAsJson().getString("codeService");
Future<String> futureQuery=getServiceQuery(codeService);
Future<JsonObject> futureParams = getServiceParams(codeService);
CompositeFuture.all(futureQuery,futureParams).setHandler(r->{
System.out.println(futureQuery.result());
System.out.println(futureParams.result());
});
}
public static Future<JsonObject> getServiceParams(String codeService){
Future<JsonObject> future=Future.future();
JsonObject params = new JsonObject();
params.put("QUERY", Queries.DB_SELECT_SERVICE_PARAMS);
params.put("PARAMS", new JsonArray().add(codeService));
DB.select(params, res -> {
if (res.succeeded()) {
future.complete(res.result());
} else {
future.fail(res.cause().getMessage());
}
});
return future;
}
public static Future<String> getServiceQuery(String codeService){
Future<String> future = Future.future();
JsonObject params = new JsonObject();
params.put("QUERY", Queries.DB_SELECT_SERVICE_QUERY);
params.put("PARAMS", new JsonArray().add(codeService));
System.out.println(params);
DB.select(params, res -> {
if (res.succeeded()) {
// query = res.result().getJsonArray("results").getJsonArray(0).getString(0);
future.complete(res.result().getJsonArray("results").getJsonArray(0).getString(0));
} else {
future.fail(res.cause().getMessage());
}
});
return future;
}
3つのオプションがあります。
2番目は、かなり高速で、わずかな労力でAPI Cを追加できるため、最良のソリューションです。