views:

1087

answers:

6

What is the best way to wait for multiple asynchronous callback functions to finish in Java before continuing. Specifically I'm using GWT with AsyncCallback, but I think this is a generic problem. Here's what I have now, but surely there is cleaner way...

    AjaxLoader.loadApi("books", "0", new Runnable(){
        public void run() {
            bookAPIAvailable = true;
            ready();
        }}, null);
    AjaxLoader.loadApi("search", "1", new Runnable(){
        public void run() {
            searchAPIAvailable = true;
            ready();
        }}, null);


    loginService.login(GWT.getHostPageBaseURL(), new AsyncCallback<LoginInfo>() {
        public void onSuccess(LoginInfo result) {
            appLoaded  = true;
            ready();
        }
    });

private void ready() {
    if(bookAPIAvailable && searchAPIAvailable && appLoaded) {
                // Everything loaded
    }
}
+1  A: 

The java.util.concurrent package contains the support you need.

Make sure your async method returns a java.util.concurrent.Future object.

Then loop through the java.util.concurrent.Future objects and execute the get() method on each of them.

Updated

I believe your best alternative is to send one async job to the server. Then split the job in many processes, execute them separately with the java.util.concurrent API and wait for all processes to finish before returning answer back to the GWT client.

Advantages with doing the thread logic on the server side is that the service is reusable from other GUI implementations and easier to test.

Espen
Question is specific to GWT even though the title said java, and java.util.concurrent package is not emulated by GWT. I updated the title after you answered, so not exactly your fault.
sri
Thanks for the info about the GWT emulator!
Espen
Sorry about not making that more specific. I didn't think about solutions that would use GWT incompatible packages.
+1  A: 

First and foremost - don't ever get into such a situation. Redesign your RPC services such that every user flow/screen requires at most a single RPC call to work. In this case, you are making three calls to the server, and its just a waste of bandwidth. The latency will just kill your app.

If you can't and really need a hack, use a Timer to periodically poll if all data has downloaded. The code you pasted above assumes login() method will be the last to finish - which is wrong. Its may be the first to finish, and then your app will be in an indeterminate state - which is very difficult to debug.

sri
Can you explain why you think this code assumes the login() method will be the last to finish. Please note that the ready() method will be called three times--only evaluating true on the last call. I've been using this code successfully and I don't think there is ordering problem, but correct me if I'm wrong...
Also, I agree ideally a single RPC call would be ideal, but it's not always possible or practical. In this case, the first two asynchronous calls are not even RPC calls, but are requests to load two separate Google APIs and there is no way that I can combine them.
I think he was saying that if you're waiting for three RPCs to finish, then that means you're making three RPC servers to the server. Instead, make one RPC and wait for one response. The gwt-dispatch (http://code.google.com/p/gwt-dispatch/) project makes batching like this much easier.
Jason Hall
+2  A: 

Like @Epsen says, Future is probably what you want. Unfortunately, I don't believe Futures are GWT-compatible. The gwt-async-future project claims to bring this functionality to GWT, though I've never tried it. It may be worth a look.

Jason Hall
A: 
Enmanuel Rivera
+1  A: 

Just tossing up some ideas:

The callbacks fire some GwtEvent using the HandlerManager. The class containing the ready methods is registered with the HandlerManager as an EventHandler for the events fired by the callback methods, and holds the state (bookAPIAvailable, searchAPIAvailable, appLoaded).

When a event arrives that specific state is changed, and we check if all the states are as desired.

For an example using the GWTEvent, HandlerManager and EventHandler, see http://www.webspin.be/?p=5

davyM
A: 

Ideally, you want to do as other posters have stated and do as much as you can in a single async call. Sometimes you have to do a bunch of separate calls. Here's how:

You want to chain the async calls. When the last async completes (login), all the items are loaded.

    final AsyncCallback<LoginInfo> loginCallback = new AsyncCallback<LoginInfo>() {
        public void onSuccess(LoginInfo result) {
            //Everything loaded
            doSomethingNow();
        }
    };
    final Runnable searchRunnable = new Runnable() {
        public void run() {
            loginService.login(GWT.getHostPageBaseURL(), loginCallback);
        }
    };

    final Runnable booksRunnable = new Runnable() {
        public void run() {
            AjaxLoader.loadApi("search", "1", searchRunnable, null);
        }
    };

    //Kick off the chain of events
    AjaxLoader.loadApi("books", "0", booksRunnable, null);

Cheers,

--Russ

Russ Sherk