18.11.2010

GWT: Controlling async processes

In this article, I present a simple class which makes it easy to start async processes, show its state to the user and handle the results.

Perhaps one of the most appealing features of GWT is the handling of async remote calls. It is really easy. Recently I realized the following pattern often used in my apps: Upon a click, an async call is triggered and after that one finished, the user interface is updated. So I have to add a ClickHandler, start the RPC call and specify an AsyncCallback. Simple enough.

On the other hand, I have lots of these interactions. And I want to display a nice little spinning wheel beside the Anchor starting the request as long as it runs. Ah... and I want a server error, if happened, to be displayed beneath that wheel.

Here the game becomes less nice and code less clean. This is why I started organizing it around the following little component:

public abstract class ProgressFlag<T> extends Image implements
HasClickHandlers, ClickHandler, AsyncCallback<T> {

public ProgressFlag() {
super("/progress.gif");
setVisible(false);
}

@Override
public void onFailure(Throwable caught) {
finish();
Window.alert(caught.getMessage());
}

@Override
public void onSuccess(T result) {
onReady(result);
finish();
}

private void finish() {
setVisible(false);
}

public abstract void onReady(T result);

public abstract void onStart();

@Override
public void onClick(ClickEvent event) {
setVisible(true);
onStart();
}
}

As you can see, this little component acts as a ClickHandler as well as an AsyncCallback and redirects to new abstract methods. Now, you can simply implement and use it:

private ProgressFlag<Person> savePerson =
new ProgressFlag<Person>() {






public void onStart() {
          srv.savePerson(person, this);
        }
public void onReady(Person result) {
          Window.alert("Person saved: "+person.getName());
        }
}
...
button.addClickHandler(savePerson);
add(button);
add(savePerson);
So now I only have to state how the call is started and what should happen when it succeeds. Errors are always handled the way I want to and I have my spinning wheel which is an important feature of a UI with asynchroneous RPC.