我正在寻找一种在调用异步服务时自动显示和隐藏“加载”消息的方法,所以不要这样做:
showLoadingWidget();
service.getShapes(dbName, new AsyncCallback() {
public void onSuccess(Shape[] result) {
hideLoadingWidget();
// more here...
}
public void onFailure(Throwable caught) {
hideLoadingWidget();
//more here
}
});
我想这样做,但仍然会在完成时显示和隐藏消息。
// 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
}
});
简而言之,我想改变异步调用的行为。感谢您提出所有可能的建议。
丹尼尔
答案 0 :(得分:19)
您可以将调用本身包装在处理显示加载消息的对象中,可能会在错误或其他情况下重试几次。像这样:
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
}
...
}
要拨打电话,请执行以下操作:
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
您可以使用代码对此进行扩展,以检测需要很长时间并显示某种忙碌指示的呼叫等。
答案 1 :(得分:4)
以下AsyncCall
是我目前使用的(受David Tinker解决方案的启发)。而不是重试,这需要一些RPC调用需要很长时间才能返回,并且如果在指定的超时之前没有返回调用,则显示一个加载指示符。
此AsyncCall
还会跟踪当前正在进行的RPC调用次数,并且只有在返回所有RPC调用时才会隐藏加载指示符。使用David的解决方案,加载指示器可能会被早期的RPC调用隐藏,即使另一个仍在进行中。这个if课程假定加载指示器小部件是应用程序的全局,这在我的情况下。
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
}
}
答案 2 :(得分:3)
完整示例(罗伯特)
public abstract class AsyncCall<T> implements AsyncCallback<T>
{
public AsyncCall()
{
loadingMessage.show();
}
public final void onFailure(Throwable caught)
{
loadingMessage.hide();
onCustomFailure(caught);
}
public final void onSuccess(T result)
{
hideLoadingMessage();
onCustomSuccess(result);
}
/** the failure method needed to be overwritte */
protected abstract void onCustomFailure(Throwable caught);
/** overwritte to do something with result */
protected abstract void onCustomSuccess(T result);
}
答案 3 :(得分:1)
您可以创建一个默认的回调超类,它在其构造函数中接受LoadingMessage
对象参数,并为子类提供钩子方法,例如: onSuccess0
和onFailure0
。
实施类似于:
public final void onFailure(Throwable caught) {
loadingMessage.hide();
onFailure0(caught);
}
protected abstract void onFailure0(Throwable caught);
答案 4 :(得分:1)
这是我的版本,与上面的版本非常相似,但有一些差异
public abstract class LoadingAsyncCallback<T> implements AsyncCallback<T> {
/**
* Override this method and call the async service method providing the arguments needed.
* @param args
*/
public abstract void callService(Object... args);
/**
* Call execute() to actually run the code in overriden method callService()
* @param args: arguments needed for callService() method
*/
public void execute(Object... args) {
//your code here to show the loading widget
callService(args);
}
@Override
public void onFailure(Throwable caught) {
//your code here to hide the loading widget
onCallbackFailure(caught);
}
@Override
public void onSuccess(T result) {
//your code here to hide the loading widget
onCallbackSuccess(result);
}
public abstract void onCallbackFailure(Throwable caught);
public abstract void onCallbackSuccess(T result);
}
一个简单的例子如下:
MyServiceAsync myServiceAsync = GWT.create(MyService.class);
LoadingAsyncCallback loadingAsyncCallback = new LoadingAsyncCallback() {
@Override
public void callService(Object... args) {
myServiceAsync.someMethod((String) args[0], (String) args[1], this);
}
@Override
public void onCallbackFailure(Throwable caught) {
}
@Override
public void onCallbackSuccess(Object result) {
}
};
String name = "foo";
String login = "bar";
loadingAsyncCallback.execute(name, login );
答案 5 :(得分:0)
如果有人在RPC调用期间寻找标记屏幕元素(窗口小部件/组件)为忙的方法,我已经实现了{{3} }。
它会禁用该组件并插入带有small utility的“div”。当然,这一切都可以解除。
在撰写本文时,这是应用于div的样式:
@sprite .busySpinner {
gwt-image: "spinnerGif";
background-repeat: no-repeat;
background-position: center;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 10000; /* Something really high */
}
实用方法:
/**
* Disables the given component and places spinner gif over top.
*/
public static void markBusy(final Component c) {
c.disable();
ensureNotBusy(c);
// NOTE: Don't add style to the component as we don't want 'spinner' to be disabled.
c.getElement().insertFirst("<div class='" + STYLE.busySpinner() + "'/>");
}
/**
* Enables the given component and removes the spinner (if any).
*/
public static void clearBusy(Component c) {
c.enable();
if (!ensureNotBusy(c)) {
GWT.log("No busy spinner to remove");
}
}
private static boolean ensureNotBusy(Component c) {
Element first = c.getElement().getFirstChildElement();
if (first != null && first.removeClassName(STYLE.busySpinner())) {
first.removeFromParent();
return true;
}
return false;
}