I am looking for a way to automate showing and hiding a 'loading' message when calling an async service, so instead of doing this:
showLoadingWidget();
service.getShapes(dbName, new AsyncCallback() {
public void onSuccess(Shape[] result) {
hideLoadingWidget();
// more here...
}
public void onFailure(Throwable caught) {
hideLoadingWidget();
//more here
}
});
I'd like to do just this, but still show and hide the message at completion.
// this should be gone: showLoadingWidget();
service.getShapes(dbName, new AsyncCallback() {
public void onSuccess(Shape[] result) {
// this should be gone: hideLoadingWidget();
// more here...
}
public void onFailure(Throwable caught) {
//this should be gone: hideLoadingWidget();
//more here
}
});
In short I would like to change the behavior of the async calls. Thank you for all the possible suggestions.
Daniel
The following AsyncCall
is what I'm currently using (inspired by David Tinker's solution). Instead of retrying, this expects some RPC calls to take long time to return and displays a loading indicator if the call haven't returned before a specified timeout.
This AsyncCall
also keeps track of the number of RPC calls currently in progress, and only hides the loading indicator if all RPC calls have returned. With David's solution, the loading indicator might be hidden by an earlier RPC call returning even though another is still in progress. This if course assumes the loading indicator widget is global for the app, which it is in my case.
public abstract class AsyncCall<T> {
private static final int LOADING_TOLERANCE_MS = 100;
private static int loadingIndicatorCount = 0;
private Timer timer;
private boolean incremented;
private boolean displayFailure;
public AsyncCall(boolean displayFailure) {
this.displayFailure = displayFailure;
timer = new Timer() {
@Override
public void run() {
if (loadingIndicator++ == 0)
// show global loading widget here
incremented = true;
}
};
timer.schedule(LOADING_TOLERANCE_MS);
call(new AsyncCallback<T>() {
@Override
public void onSuccess(T result) {
timer.cancel();
if (incremented && --loadingIndicatorCount == 0)
// hide global loading widget here
callback(result);
}
@Override
public void onFailure(Throwable caught) {
timer.cancel();
if (incremented && --loadingIndicatorCount == 0)
// hide global loading widget here
if (AsyncCall.this.displayFailure)
// show error to user here
}
});
protected abstract void call(AsyncCallback<T> cb);
protected void callback(T result) {
// might just be a void result or a result we
// wish to ignore, so do not force implementation
// by declaring as abstract
}
}
You could wrap the call itself in an object that handles displaying the loading message, maybe retrying a few times on errors or whatever. Something like this:
public abstract class AsyncCall<T> implements AsyncCallback<T> {
/** Call the service method using cb as the callback. */
protected abstract void callService(AsyncCallback<T> cb);
public void go(int retryCount) {
showLoadingMessage();
execute(retryCount);
}
private void execute(final int retriesLeft) {
callService(new AsyncCallback<T>() {
public void onFailure(Throwable t) {
GWT.log(t.toString(), t);
if (retriesLeft <= 0) {
hideLoadingMessage();
AsyncCall.this.onFailure(t);
} else {
execute(retriesLeft - 1);
}
}
public void onSuccess(T result) {
hideLoadingMessage();
AsyncCall.this.onSuccess(result);
}
});
}
public void onFailure(Throwable t) {
// standard error handling
}
...
}
To make the call do something like this:
new AsyncCall<DTO>() {
protected void callService(AsyncCallback<DTO> cb) {
DemoService.App.get().someService("bla", cb);
}
public void onSuccess(DTO result) {
// do something with result
}
}.go(3); // 3 retries
You could extend this with code to detect calls that are taking a long time and display a busy indicator of some kind etc.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With