Petre Popescu
Petre Popescu

Reputation: 2002

Two Promises simultaneously

I am working on a Play application that does a lot of communication (retrieving of data) from other 3rd party services. What I am trying to do is "combine" the results of each request.

To be more precise. I have three external WSRequests each encapsulated in a class that has a method that will execute the request, retrieve the JSON String and return a list of objects that are essentially deserialized versions for the JSON. Extremely simplified, it can be reduced to something like this.

Class A {
    function F.Promise<List<ObjA>> call();
}
Class B {
    function F.Promise<List<ObjB>> call(Integer id);
}
Class C {
    function F.Promise<List<ObjC>> call();
}

From my controller, I do calls to objects of A, B and C, do some processing and return a result which is a JSON String of another object (lets call it ObjD).

In my controller, I have something like this:

public F.Promise<Result> compute() {
   A a = new A();
   B b = new B();
   C c = new C();

  List<ObjA> objAList = a.call().get(TIMEOUT);
  List<ObjB> objBList = b.call(objAList.get(0).getId()).get(TIMEOUT);
  List<ObjC> objCList = c.call().get(TIMEOUT);

  // compute something using the three lists. This will create an object objD

  return F.Promise.promise(() -> ok(Json.toJson(objD)));
}

The result for List is dependents on the result from A (so they can't be called simultaneously). Obviusly, i can simply thing like this:

public F.Promise<Result> compute() {
    A a = new A();
    B b = new B();
    C c = new C();

    List<ObjC> objCList = c.call(0.get(TIMEOUT);
    return a.call().map(objAList -> {
       List<ObjB> objBList = b.call(objAList.get(0).getId()).get(TIMEOUT);
       // compute and make objD
       return Json.toJson(objD);
    });
}

What however I do not know is if (and how) to execute the call to A and C simultaneously and start the rest of the processing once both are received. So instead of doing the call to C, waiting for the result and only after that doing the call to A, I want to be able to trigger both A and C calls and once results from both are received to do the computation needed with the results.

Thanks

Upvotes: 1

Views: 467

Answers (1)

Anton Sarov
Anton Sarov

Reputation: 3748

WS Requests in Play are executed async by default.

The first problem here is that you are forcing Play to wait / to be sync. Using F.Promise#get() is not a good idea. Instead you should used map/flatMap to manipulate the response - which you partly do with the call to service A.

If you look further in the F.Promise API - there is a method called sequence - which you can use - just collect all your promises in a list and call F.Promise.sequence(myPromises) - you would get a single promise for the list of results - which you can then use to produce your object D.

Upvotes: 1

Related Questions