Автоматический индикатор загрузки при вызове асинхронной функции
Я ищу способ автоматизировать показ и скрытие сообщения "загрузка" при вызове службы async, поэтому вместо этого:
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
}
});
Короче говоря, я хотел бы изменить поведение асинхронных вызовов. Благодарим вас за все возможные предложения.
Daniel
Ответы
Ответ 1
Вы можете обернуть сам вызов в объекте, который обрабатывает отображение загружаемого сообщения, возможно, несколько раз повторяет ошибки или что-то еще. Что-то вроде этого:
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
Вы можете расширить это с помощью кода, чтобы обнаруживать вызовы, которые занимают много времени, и отображать индикатор занятости и т.д.
Ответ 2
Следующий AsyncCall
- это то, что я использую в настоящее время (вдохновленный решением Дэвида Тинкера). Вместо повторной попытки ожидается, что некоторые вызовы RPC потребуют много времени для возврата и отобразит индикатор загрузки, если вызов не вернулся до указанного таймаута.
Этот AsyncCall
также отслеживает количество вызовов RPC, которые в настоящее время выполняются, и только скрывает индикатор загрузки, если все вызовы RPC вернулись. С решением Дэвида индикатор загрузки может быть скрыт более ранним вызовом RPC, даже если он еще продолжается. Это, если курс предполагает, что виджет индикатора загрузки является глобальным для приложения, что в моем случае.
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
}
}
Ответ 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);
}
Ответ 4
Вы можете создать суперкласс обратного вызова по умолчанию, который принимает конструктор объекта LoadingMessage
в своем конструкторе и предоставляет методы hook для подклассов, например. onSuccess0
и onFailure0
.
Реализация будет похожа на:
public final void onFailure(Throwable caught) {
loadingMessage.hide();
onFailure0(caught);
}
protected abstract void onFailure0(Throwable caught);
Ответ 5
Вот моя версия, почти такая же, как и выше, но с некоторыми отличиями
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 );
Ответ 6
В случае, если кто-то ищет способ пометить элемент экрана (виджет/компонент) как занятый во время вызова RPC, я реализовал небольшая утилита.
Он отключает компонент и вставляет '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;
}