9

I have a situation where I call an external API A and use its response to feed to request of API B and call it and afterwards return the response to caller of API A. Something like below

   method(){
    response = call API A
    }

    method_for_API_A(){
      handler() ->{
      API_B
      }
    return response;
    }

    method_for_API_B(){
    //code to call API B
    }

What I am facing here is that API A method is returning response without waiting for getting response from B.

I checked about executeBlocking method of vert.x and also tried of using 'blocking queue' but unable to achieve what I am intended to do. Can someone please direct me to correct way of doing it.Thanks in advance.

EDIT: Just to explain the exact scenario

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;
}

}

I am using vert.x framework with Java. Now what happens during execution is, flow comes to STEP 1, initiate API A call. Goes to STEP 2 (without waiting for 'respFromApiA') and makes call to API B (which fails eventually because respFromApiA is NULL). And finally flow goes to STEP 3 and return from here. (without waiting for outcomes of API A and API B). If we see the print order it will be something like this

PRINT FINAL Response
PRINT API A response
PRINT API B response

What I am trying to achieve?

Wait for API A response.
Make call to API B. Wait for API B response.
Return response got from API B.

I hope I am able to make clear this time. Please let me know if you need further inputs.

ManoDestra
  • 6,325
  • 6
  • 26
  • 50
tausif
  • 672
  • 1
  • 6
  • 15
  • Why don't you do both calls in `method` sequentially? – Fildor May 13 '16 at 10:18
  • I guess you are doing non-blocking calls. They most likely return their responses in a callback (I don't know vert.x, sorry). So you need to find either a possibility to tell vert.x that you want those calls to be blocking and return the result or you need to use the callbacks. – Fildor May 13 '16 at 13:05
  • Maybe if you showed more of code (real code) then someone with experience in vert.x could help ... – Fildor May 13 '16 at 13:08

3 Answers3

20

Vert.x is highly asynchronous. Most operations will in fact return immediately but their results will be available to a Handler at a later point in time. So far so good. If I understand you correctly than you need to call B in the Handler of A. In this case A needs to be finished and the result would be available before you call B:

callA(asyncResultA -> {
  System.out.println("Result A: " + asyncResultA.result());

  callB(asyncResultB -> {
    System.out.println("Result B:" + asyncResultB.result());
  });
});

But what you are trying is to make something asynchronous synchron. You can not and should not try to make a asynchronous result available in the main program flow — that wouldn't work.

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 can't really return a result because it's calculated asynchronously. The result is only available for the Handler of Call_API_A (see my example above). Same for Call_API_B – so you can't return the result of Call_API_B. The caller of your class would also need to call your class with a Handler.

Now some additional information. In your case you have the problem that multiple asynchronous results depend on each other. Vert.x provides a much more convenient way to handle asynchronous results — the so called Futures. A Future (sometimes called Promise but in the Java world they are called Future) is a placeholder for results of asynchronous calls. Read about them in the documentation.

With a Future you can do something like this:

Future<...> callAFuture = Future.future();
callA(asyncResultA -> {
  if (asyncResultA.succeeded()) {
    System.out.println("A finished!");
    callAFuture.complete(asyncResultA.result());
  } else {
    callAFuture.fail(asyncResultA.cause());
  }
});

So instead of trying to return the asynchronous result of B in a synchronous way you should return a Future so the callee of your class could register for the asynchronous result of both A and B.

I hope this helps.

Edit: Future as return value

Lets say you want to wrap callA with so you can work with a Future. You could do it this way:

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;
}

The Caller of this function can use the Future like this:

Future<String> doSomethingFuture = doSomethingAsync();
doSomethingFuture.setHandler(somethingResult -> {
  // ... doSomethingAsync finished
});

It also possible to compose multiple Future if you want to do them concurrently but they don't dependent on each other:

CompositeFuture.all(futureA, futureB).setHandler(connections -> {
  // both Futures completed
});

If you work in a asynchronous environment like Vert.x most of time you work with a soon-to-be-available-result aka Future. And in the Handler of a Future you often do another asynchronous call. You wrap a Future with a Future like the example of callB in callA's Handler.

How would you return the asynchronous result of a Future as an HTTP Response? Like this:

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);
    }
  });
});
alexvetter
  • 1,998
  • 2
  • 16
  • 42
  • Thanks @Alexvetter for answering me. I agree with the logic of calling API_B in handler of API_A. This I tried already and it works.Mistake I am doing is, trying to return response to caller of class synchronously, which should be handled via a handler. Just to clear one point, at the end I have to return the response of API_B to the caller.You said about using future object. So we just put return type as Future and return the future object to caller to get the response? – tausif May 14 '16 at 08:43
  • Exactly, you should play a little bit with `Future` and its method `setHandler`. – alexvetter May 14 '16 at 08:46
  • Because Vert.x is highly asynchronous your application will hand `Futures` around and compose them as you need. It's that you don't block the Event Loop! – alexvetter May 14 '16 at 09:23
  • Hi, @alexvetter can you please show me or direct to an example of using future object. Is it like whenver i need to return something from API call I will store the value in future object use it as needed? Thanks. – tausif May 16 '16 at 05:32
  • @tausif added some more example, hope it helps. – alexvetter May 17 '16 at 12:00
  • that really helped. Thank you very much for describing it so aptly. – tausif May 22 '16 at 17:10
  • @tausif I'm happy I could help. I would really appreciate if you upvote and/or accept the answer. This is how you show others that it helped you. – alexvetter May 22 '16 at 18:04
5

I have used Future to return some results to use it again in other methodes, this is my implementation i hope it helps someone :

 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;
}
OLH
  • 194
  • 3
  • 12
0

You have three options:

  1. Do the API A call and in the callback do the API B call.
  2. Use async framework(https://spring.io/guides/gs/async-method/) of your choice that will run both api calls in parallel.
  3. Same as first, but with promises.

Second is the best solution, since it will be considerably faster and you could add API C with little effort

Martin Gottweis
  • 2,721
  • 13
  • 27
  • As far as I understood the question, there is some result from Call to B that is needed as Param to Call to A. So parallel call does not really make sense. – Fildor May 13 '16 at 10:33
  • I understood that the output of A and B is supposed to do something. Maybe my bad. – Martin Gottweis May 13 '16 at 11:36
  • I was referring to this part of the question: "I call an external API A and use its response to feed to request of API B". Maybe @tausif can put this a little bit clearer. – Fildor May 13 '16 at 11:38
  • Yea, right. Then probably B needs a callback and the return statement from A needs to be in the callback from B. – Martin Gottweis May 13 '16 at 11:49
  • Hi @Fildor please check the edit section of my question. – tausif May 13 '16 at 12:45