Parallel asynchronous calls in GWT


(20 comments)
November 29th 2010


The asynchronous nature of GWT makes for more powerful and usable web applications, but as a developer, it can take a little while to get used to. Here's one example: imagine a scenario where some client-side class needs to call two service operations before it can do something. Simple, right? In a synchronous world, it would be:

public void someMethod() {
  Service someSerive = new SomeService();
  String str1 = someService.foo();
  String str2 = someService.bar();
  doSomething(str1, str2);
}

In GWT, things are necessarily a little trickier. Using the standard RPC plumbing, calls from the client to a remote service are handled asynchronously, via GWT's AsyncCallback mechanism. For example:

someService.foo(new AysncCallback() {
    public void onFailure(Throwable caught) {}

    public void onSuccess(final String str1) {
      // do something here;
    }
});

This is relatively straight-forward. If we wanted to make two calls to some service, however, then do something after both have completed, our first instinct might be to just chain the service calls - e.g. when one service completes, call the second:

public void someGwtClientSideMethod() {
  SomeServiceAsync someService = GWT.create(SomeService.class);
  someService.foo(new AsyncCallback() { 

    public void onFailure(Throwable caught) {}

    public void onSuccess(final String str1) {
      service.bar(new AsyncCallback() { 

        public void onFailure(Throwable caught) {}

        public void onSuccess(String str2) {
          doSomething(str1, str2);
        }
      });
    });
  }

While this works, it's not ideal. First, it's really ugly...and this is just for a case with 2 chained async calls - imagine if you had 3 or 4! Second, it's slower than it needs to be, since the calls are made serially rather than in parallel.

One obvious solution is to just combine the two service operations into one (an argument for this here) - only one call is made to the server, and you don't have to bother with the ugly nesting:

public void someGwtClientSideMethod() {
  SomeServiceAsync someService = GWT.create(SomeService.class);
  someService.fooAndBar(new AsyncCallback() { 

    public void onFailure(Throwable caught) {}

    public void onSuccess(final FooBarResult fbr) {
      doSomething(fbr.getFooResult(), fbr.getBarResult());
    }
  });
}

This is better, but unfortunately combining service operations like this isn't always possible (or practical). Services may not be under your control...and even if they are, it's a lot of work to aggregate operations, and could result in a pretty complex/messy service if there are many such combinations.

Another possible solution would be to make the calls in parallel, and then only doSomething() when both service calls return. I developed two classes, ParallelCallback and ParentCallback, for this exact purpose. Using these, the code in the client would look like this:

public void someGwtClientSideMethod() {
  SomeServiceAsync someService = GWT.create(SomeService.class);
  ParallelCallback fooCallback = new ParallelCallback();
  ParallelCallback barCallback = new ParallelCallback();
  ParentCallback parent = new ParentCallback(fooCallback, barCallback) {
    public void handleSuccess() {
      doSomething(getCallbackData(0), getCallbackData(1));
    }
  };
  someService.foo(fooCallback);
  someService.bar(barCallback);
}

Without getting too mired in the implementation guts, essentially each service method has its own ParallelCallback, which is registered with a ParentCallback. When the service has completed, the ParallelCallback then informs the parent it's done, and when the parent has heard back from each of is children, it calls its own handleSuccess() method, overridden and implemented by you. Return data from the individual service ParallelCallbacks can be extracted using the getCallbackData() method.

While this solution definitely still has some conceptual load, it eliminates the double-AsyncCallback-nesting, so seems a little simpler (to me!) than the chaining solution above. Also, as mentioned, RPC calls are made in parallel, so the overall latency should be less.

Anyway, the code is available here and here if you're interested. Let me know what you think, or if you've found another better way to solve this issue.

Note: this issue was documented on StackOverflow, and there are some good insights there as well (like check out gwt-dispatch for one).

I'm an "old" programmer who has been blogging for almost 20 years now. In 2017, I started Highline Solutions, a consulting company that helps with software architecture and full-stack development. I have two degrees from Carnegie Mellon University, one practical (Information and Decision Systems) and one not so much (Philosophy - thesis here). Pittsburgh, PA is my home where I live with my wife and 3 energetic boys.
I recently released a web app called TechRez, a "better resume for tech". The idea is that instead of sending out the same-old static PDF resume that's jam packed with buzz words and spans multiple pages, you can create a TechRez, which is modern, visual, and interactive. Try it out for free!
Got a Comment?
Comments (20)
Anees-ur-Rehman
November 29, 2010
I am wondering why you need parallel calls. This sound to me a bad design. You can perform such complication on the facade layer and use one call on the GWT presentation layer
Nicolas
November 30, 2010
Just call the 2 services. Use a generic mechanism like MVP or MVC (basically a listener mechanism or what GWT team call a bus) to handle the return of your call.
It is more generic because it’s not the code that call the request (let say on a button click) that has to know how to deal with the response.
Let imagine you clicked on the “refresh” button. If you don’t have a “refresh” service that will send back all updates in one request, maybe as a “dumb” implementation you request a refresh of you the 3 main parts of your UI. For prototyping or an intranet application a lan it may be enough.
Maybe anyway, your request sender mechanism on the client side is abble to agregate several requests to the server in one http request, so you don’t even need to make a dedicated service to deal with theses two request at the same time.
And that’s not the “refresh” button that is concerned about the new data, but the 3 differents parts of your UI. When you receive the response from the server, you convert them in UI events like “Data1Update”, “Data2Update”, “Data3Update”.
Maybe Data2 is even used in several part of the UI, to display differents things. Let say it is a mail. The title of the browser window maybe has to be updated with the title of the mail. Then the mail list has to be updated with the title and sender of the mail. And the view that show the mail has to be updated with the mail body.
Jim Kiley
November 30, 2010
As Ben suggests, there are times where the UI-side developer doesn’t have full control of the service layer.
These classes would be useful to an end developer when the client-side code needs data from two different services in order to do further processing of its own and then put data on the screen. Therefore this isn’t a situation where each result can simply be dropped onto the user interface as it comes up.
Ben
November 30, 2010
Thanks for the comments!
Anees-ur-Rehman: I did consider the facade approach (“combining service operations”) in the post, but maybe too subtly. In some cases, using a facade is preferable…but, in my experience, it’s not always possible or practical. Again, services may not be in your control. Also, combining operations can be tedious and can result in more complexity. For example, if you have two service operations getX() and getY() that return objects of type X and Y respectively, then a combined operation in some facade, say, getXandY(), would either have to return a composite DTO that contains references to both X and Y (e.g. XY), or some other generic data structure that contains the X and Y objects (e.g. Map, List, etc.). In either case, extra code needs to written to assemble this new object (e.g. xy.setX(x), map.put(“x”, x), etc.) and dissessemble (e.g. xy.getX(), map.get(x), etc.). Lastly, if you have a large application, then this can result in a copious number of combined service methods in your facade (e.g. getXandY(), getXandZ(), getWandZandS(), etc.). It reminds me of the early J2EE days using SessionFacades…and it makes me cringe a bit.
Nicolas: Good thoughts. I believe the approach here is actually orthogonal to the MVC/MVP pattern. We use the MVP pattern and this solution together.
Jim: Thanks for helping to clarify!
Jorge Lee
November 30, 2010
Another alternative is to use command style RPC calls. By combining async calls to a same interface gives you the opportunity to aggregate your calls in a single request like a batch. This idea was proposed during Google I/O 2009
Ashton
December 02, 2010
Thanks for the post!
You learn something new every day, and today I learned you could have arguments of variable length and synchronized methods in java :)
Although it doesn’t say much for my brief and patchy java career, your post and code examples will help me write better asynchronous code with gwt..
Thanks!
Ashton
February 20, 2011
Just a follow up here. This post helped me solve a little bit different challenge. Using a synchronize object to keep track of returning requests that are not controlled by a parent object.
I have an Activity that calls a service to get a list of objects. each object in the list will need to execute a request to get another list of different objects. Each layer is done within a different widget.
Activity has ObjectAsWidget (Plural) which holds a List
Each ObjectAWidget (singular) has a List
The Activity needs to know when all the ObjetB’s for each ObjectA have been returned (Because one of the ObjectB’s is a newly added ObjectB and we want our encompassing ScrollPanel to scroll to it’s position)
So if we don’t wait until all the ObjectB’s are returned, we wont be able to tell the ScrollPanel where to scroll to because we won’t have the DOM element for the ObjectB….
So using s similar ObjectBSynchronizer just needs to now the number of ObjectA’s and then when each List for an ObjectA is returned we tell the ObjectBSynchronizer
When all ObjectA’s have a List we fire an event that tells our Layout Shell to scroll to the element corresponding to ObjectB
I guess there are challenges to embracing the completely Asynchronous world of GWT. A little Synchronization is nice every now and then..
Ben
February 21, 2011
Thanks Ashton! Interesting issue…thanks for sharing that.
Maghesh
March 18, 2011
Why the download code says “S3RuntimeException” on ParentCallback.java ?
My compilations gives error. Then i changed this tio “RuntimeException”
Please correct this if this is a mistake
Ben
March 18, 2011
Maghesh – good catch. I fixed it now. Sorry about that.
Ashish
March 18, 2011
This is very good…Could not find a better way then this.
THANKS!!!!!
Rix
November 28, 2011
Thanks alot very helpful
Dustin
December 02, 2011
Hi Ben,
Thanks for the post, it gave me some other way to think about my problem.
I have a file browser in GWT, but the service provides ‘pages’ of data, which means I need to loop the asynchronous service calls. In your opinion, is this an appropriate way to do this? Create a list of callbacks and wait until they’re all done?
Thank you!
Nabeel
March 21, 2012
Extremely well written and explained.
Good job. Exposes a new pattern for a newbie like myself on AsyncCallback subject.
girish
March 21, 2012
I din’t like the fact that My Service methods should know about ParallelCallBack.
Joe
June 06, 2012
Nice implementation!
July 07, 2012
Thanks for your article.
It’s a good solution when you can’t rewrite services.
steve
August 21, 2012
Thanks for the post Ben, this stopped me writing some nasty code!
Derek Farren
July 21, 2013
Thanks, this very useful.
Steevy
July 25, 2013
This is a good idea for a “light” application because you don’t mention services complexity(on the server side).
What will you do if one of the parrallelCallback fails ? stop the other ones ?
Magic numbers aren’t efficient :” doSomething(getCallbackData(0), getCallbackData(1)); “, the code isn’t clear. If someone moves one line, he may forget changing index when calling parrallelCallbacks