Автоматический индикатор загрузки при вызове асинхронной функции - PullRequest
18 голосов
/ 21 августа 2009

Я ищу способ автоматизировать показ и скрытие сообщения «загрузки» при вызове асинхронной службы, поэтому вместо этого:

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

Ответы [ 6 ]

19 голосов
/ 21 августа 2009

Вы можете обернуть сам вызов в объект, который обрабатывает отображение сообщения о загрузке, может быть, несколько раз повторить попытку на ошибки или что-то еще. Примерно так:

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

Вы можете расширить это с помощью кода, чтобы обнаруживать длительные вызовы и отображать какой-либо индикатор занятости и т. Д.

4 голосов
/ 18 декабря 2012

Следующее 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 голосов
/ 23 февраля 2010

Полный пример (Роберт)

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); 
}
1 голос
/ 12 сентября 2014

Вот моя версия, почти такая же, как и выше, но с некоторыми отличиями

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 );
1 голос
/ 21 августа 2009

Вы можете создать суперкласс обратного вызова по умолчанию, который принимает аргумент объекта LoadingMessage в своем конструкторе и предоставляет методы перехвата для подклассов, например, onSuccess0 и onFailure0.

Реализация будет похожа на:

public final void onFailure(Throwable caught) {
    loadingMessage.hide();
    onFailure0(caught);
}

protected abstract void onFailure0(Throwable caught);
0 голосов
/ 28 мая 2013

В случае, если кто-то ищет способ пометить элемент экрана (виджет / компонент) как занятый во время вызова 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...