How to block thread to wait for response in vert.x?

tausif picture tausif · May 13, 2016 · Viewed 15k times · Source

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.

Answer

alexvetter picture alexvetter · May 13, 2016

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