Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clean way in GWT/Java to wait for multiple asynchronous events to finish

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
    }
}
like image 768
gerdemb Avatar asked Jun 07 '10 20:06

gerdemb


4 Answers

I wrote two classes that solve this problem on my project. Basically, each individual callback registers with a parent. The parent waits for each child callback to complete, then fires off it's own handleSuccess().

The client code looks 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(1), getCallbackData(2));
        }
    };
    someService.foo(fooCallback);
    someService.bar(barCallback);
}

I wrote a post explaining it here: Parallel Asynchronous Calls in GWT. The implementation for these two classes is linked from that post (sorry, can't give links here because I'm a newbie user - not enough karma to include more than one link!).

like image 70
throp Avatar answered Nov 19 '22 09:11

throp


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.

like image 20
Jason Hall Avatar answered Nov 19 '22 11:11

Jason Hall


I've struggled with this myself, and I've used several methods- the 'chain' one just gets ugly (but can be improved if you create classes instead of inline classes for each method).

A variant of your own version works well for me:

int outstandingCalls = 0;
{
outstandingCalls++;
 AjaxLoader.loadApi("books", "0", new Runnable(){
    public void run() {
        ready();
    }}, null);

outstandingCalls++;
AjaxLoader.loadApi("search", "1", new Runnable(){
    public void run() {
        ready();
    }}, null);


outstandingCalls++;
loginService.login(GWT.getHostPageBaseURL(), new AsyncCallback<LoginInfo>() {
    public void onSuccess(LoginInfo result) {
        ready();
    }
    // Be sure to decrement or otherwise handle the onFailure
});
}

private void ready() {
if (--outstandingCalls > 0) return;

// Everything loaded
}

All I did was create a counter for the number of calls I'm going to do, then each async result calls ready() (be sure to do this on the failure methods too, unless you're going to do something different)

In the ready method, I decrement the counter and see if there are still outstanding calls.

It's still ugly, but it lets you add calls as needed.

like image 4
Sasquatch Avatar answered Nov 19 '22 10:11

Sasquatch


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.

like image 2
Sripathi Krishnan Avatar answered Nov 19 '22 11:11

Sripathi Krishnan