代码之家  ›  专栏  ›  技术社区  ›  gerdemb

GWT/Java中等待多个异步事件完成的干净方法

  •  39
  • gerdemb  · 技术社区  · 16 年前

        AjaxLoader.loadApi("books", "0", new Runnable(){
            public void run() {
                bookAPIAvailable = true;
                ready();
            }}, null);
        AjaxLoader.loadApi("search", "1", new Runnable(){
            public void run() {
                searchAPIAvailable = true;
                ready();
            }}, null);
    
    
        loginService.login(GWT.getHostPageBaseURL(), new AsyncCallback<LoginInfo>() {
            public void onSuccess(LoginInfo result) {
                appLoaded  = true;
                ready();
            }
        });
    
    private void ready() {
        if(bookAPIAvailable && searchAPIAvailable && appLoaded) {
                    // Everything loaded
        }
    }
    
    8 回复  |  直到 16 年前
        1
  •  41
  •   throp    9 年前

    我在我的项目中写了两个类来解决这个问题。基本上,每个回调都向一个父回调注册。父级等待每个子级回调完成,然后触发自己的handleSuccess()。

    
    
    
    public void someGwtClientSideMethod() {
        SomeServiceAsync someService = GWT.create(SomeService.class);
        ParallelCallback fooCallback = new ParallelCallback();
        ParallelCallback barCallback = new ParallelCallback();
        ParentCallback parent = new ParentCallback(fooCallback, barCallback) {
            public void handleSuccess() {
                doSomething(getCallbackData(1), getCallbackData(2));
            }
        };
        someService.foo(fooCallback);
        someService.bar(barCallback);
    }
    

    我写了一篇帖子在这里解释: Parallel Asynchronous Calls in GWT . 这两个类的实现是从那篇文章链接的(抱歉,这里不能给出链接,因为我是一个新手-没有足够的业力来包含多个链接!)。

        2
  •  5
  •   Jason Hall    16 年前

    就像@Epsen说的, Future 可能是你想要的。不幸的是,我不相信 gwt-async-future project声称将此功能引入GWT,尽管我从未尝试过。也许值得一看。

        3
  •  4
  •   Sasquatch    14 年前

    我自己也在努力解决这个问题,我使用了几种方法——链式方法很难看(但是如果你为每个方法创建类而不是内联类的话,这种方法可以得到改进)。

    你自己版本的一个变体对我很有用:

    int outstandingCalls = 0;
    {
    outstandingCalls++;
     AjaxLoader.loadApi("books", "0", new Runnable(){
        public void run() {
            ready();
        }}, null);
    
    outstandingCalls++;
    AjaxLoader.loadApi("search", "1", new Runnable(){
        public void run() {
            ready();
        }}, null);
    
    
    outstandingCalls++;
    loginService.login(GWT.getHostPageBaseURL(), new AsyncCallback<LoginInfo>() {
        public void onSuccess(LoginInfo result) {
            ready();
        }
        // Be sure to decrement or otherwise handle the onFailure
    });
    }
    
    private void ready() {
    if (--outstandingCalls > 0) return;
    
    // Everything loaded
    }
    

    我所做的只是为我要执行的调用数创建一个计数器,然后每个异步结果调用 ready() (一定要在失败方法上也这样做,除非你要做一些不同的事情)

    它仍然很难看,但它允许您根据需要添加呼叫。

        4
  •  2
  •   Sripathi Krishnan    16 年前

    首先也是最重要的一点——不要陷入这样的境地。重新设计RPC服务,使每个用户流/屏幕最多只需要一个RPC调用即可工作。在这种情况下,您要对服务器进行三次调用,这只是浪费带宽。延迟只会杀死你的应用程序。

    如果你不能而且真的需要一个黑客,使用 Timer 定期轮询是否已下载所有数据。上面粘贴的代码 假设 login()方法将是最后一个完成的-这是错误的。它可能是第一个完成,然后你的应用程序将在一个不确定的状态-这是非常困难的调试。

        5
  •  1
  •   Davy Meers    16 年前

    回调使用HandlerManager触发一些GwtEvent。 包含ready方法的类在HandlerManager中注册为回调方法激发的事件的EventHandler,并保持状态(bookAPIAvailable、searchAPIAvailable、appLoaded)。

    当一个事件到达时,特定的状态被改变,我们检查是否所有的状态都是所需的。

    http://www.webspin.be/?p=5

        6
  •  1
  •   DeadPassive Veerababu Medisetti    14 年前

    public class CallbackCounter {
        private int outstanding;
        private final Callback<String, String> callback;
        private final String message;
    
        public CallbackCounter(int outstanding, Callback<String, String> callback, String callbackMessage) {
            this.outstanding = outstanding;
            this.callback = callback;
            this.message = callbackMessage;
        }
    
        public void count() {
            if (--outstanding <= 0) {
                callback.onSuccess(message);
            }
        }
    }
    

    然后在我的回电里我就打电话给:

    counter.count();
    
        7
  •  0
  •   Enmanuel Rivera    16 年前

    正如sri所说,最好的情况是重新设计你的应用程序,一次只调用后端一次。这避免了这种情况,并保留了带宽和延迟时间。在web应用程序中,这是您最宝贵的资源。

    已经说过GWT-RPC模型并不能真正帮助您以这种方式组织事情。我自己也遇到过这个问题。我的解决方案是实现一个计时器。计时器将每隔X秒轮询一次结果,当检索到所有预期结果时,执行流可以继续。

    PollTimer extends Timer
    {
         public PollTimer()
         {
              //I've set to poll every half second, but this can be whatever you'd like.
              //Ideally it will be client side only, so you should be able to make it 
              //more frequent (within reason) without worrying too much about performance
              scheduleRepeating(500);
         }
         public void run 
         {
              //check to see if all your callbacks have been completed
              if (notFinished)
                  return;

          //continue with execution flow
          ...
         }
    

    }

    调用RPC,然后实例化一个新的PollTimer对象。这应该能奏效。

    GWT仿真不支持java.util.concurrent中的内容。在这种情况下帮不了你。出于所有目的,您在客户端执行的所有代码都是单线程的。试着进入那种思维定势。

        8
  •  0
  •   Russ Sherk    16 年前

    理想情况下,您希望像其他海报所说的那样,在一个异步调用中尽可能多地执行操作。有时你得打一大堆单独的电话。方法如下:

        final AsyncCallback<LoginInfo> loginCallback = new AsyncCallback<LoginInfo>() {
            public void onSuccess(LoginInfo result) {
                //Everything loaded
                doSomethingNow();
            }
        };
        final Runnable searchRunnable = new Runnable() {
            public void run() {
                loginService.login(GWT.getHostPageBaseURL(), loginCallback);
            }
        };
    
        final Runnable booksRunnable = new Runnable() {
            public void run() {
                AjaxLoader.loadApi("search", "1", searchRunnable, null);
            }
        };
    
        //Kick off the chain of events
        AjaxLoader.loadApi("books", "0", booksRunnable, null);
    

    干杯,