【问题标题】:Automatic 'loading' indicator when calling an async function调用异步函数时自动“加载”指示器
【发布时间】:2010-11-21 12:28:51
【问题描述】:

我正在寻找一种在调用异步服务时自动显示和隐藏“加载”消息的方法,所以不要这样做:

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
    }
});

简而言之,我想改变异步调用的行为。感谢您提供所有可能的建议。

丹尼尔

【问题讨论】:

标签: java gwt


【解决方案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 是我目前正在使用的(灵感来自 David Tinker 的解决方案)。这不是重试,而是期望一些 RPC 调用需要很长时间才能返回,如果调用在指定的超时之前没有返回,则会显示一个加载指示符。

这个AsyncCall 还跟踪当前正在进行的 RPC 调用的数量,并且只有在所有 RPC 调用都返回时才隐藏加载指示器。使用 David 的解决方案,加载指示器可能会被较早的 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 对象参数,并为子类提供挂钩方法,例如onSuccess0onFailure0

      实现类似于:

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

      【讨论】:

      • 感谢罗伯特的建议。我不是这样,因为 GWT Async 接口需要实现 OnSucess() 和 OnFailure() 函数。
      • 我的代码只是一个骨架。以类似的方式实现 onSuccess(T result) onSuccess0(T result)。我不明白为什么这不起作用。
      【解决方案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 调用期间寻找一种方法来标记屏幕元素(小部件/组件)忙碌,我已经实现了small utility

        它禁用组件并插入一个带有particular style 的“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;
        }
        

        【讨论】:

          猜你喜欢
          • 2020-03-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多