清理GWT / Java中的方法来等待多个asynchronous事件完成

在继续之前,等待多个asynchronouscallback函数在Java中完成的最佳方法是什么? 具体来说,我使用GWT与AsyncCallback,但我认为这是一个普遍的问题。 这是我现在拥有的东西,但肯定有更干净的方式…

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

我写了两个类来解决我的项目中的这个问题。 基本上,每个callback注册与父母。 父级等待每个子callback完成,然后触发它自己的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); } 

我在这里写了一篇文章解释它: GWT中的并行asynchronous调用 。 这两个类的实现是从那个post链接的(抱歉,因为我是一个新手用户,所以不能在这里给出链接 – 没有足够的业力来包含多个链接!)。

就像@Epsen所说的, Future可能就是你想要的。 不幸的是,我不相信Future与GWT兼容。 gwt-async-future项目声称把这个function带给GWT,尽pipe我从来没有尝试过。 这可能值得一看。

我自己也在努力,而且我使用了几种方法 – “链”只是变得丑陋(但是如果您为每种方法创build类而不是内联类,则可以改进)。

你自己的版本的一个变种适合我:

 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 } 

我所做的只是创build一个计数器来处理要执行的调用次数,然后每个asynchronous结果调用ready() (除非你要做一些不同的事情,否则也一定要在失败方法上做这个)

在现成的方法,我递减计数器,看看是否还有未解决的电话。

它仍然很丑,但它可以让你根据需要添加电话。

首先也是最重要的 – 永远不要陷入这样的局面。 重新devise您的RPC服务,以便每个用户stream/屏幕最多只需一个RPC调用即可工作。 在这种情况下,您正在向服务器发出三个呼叫,而这只是浪费了带宽。 延迟将只会杀死你的应用程序。

如果你不能也真的需要破解,使用一个定时器来定期轮询是否所有的数据已经下载。 上面粘贴的代码假定 login()方法将是最后一个完成 – 这是错误的。 它可能是第一个完成,然后你的应用程序将处于一个不确定的状态 – 这是很难debugging。

只是抛出一些想法:

callback函数使用HandlerManager激发了一些GwtEvent。 包含ready方法的类在HandlerManager中注册为EventHandler,用于由callback方法触发的事件,并保存状态(bookAPIAvailable,searchAPIAvailable,appLoaded)。

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

有关使用GWTEvent,HandlerManager和EventHandler的示例,请参见http://www.webspin.be/?p=5

我做了类似于@Sasquatch的东西,而是使用了一个“CallbackCounter”对象:

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

然后在我的callback中,我只是打电话给:

 counter.count(); 

最好的情况,正如斯里所说,重新devise你的应用程序只调用一次后端。 这避免了这种情况,并且保留了带宽和等待时间。 在networking应用程序中,这是您最宝贵的资源。

说了GWT RPC模型并不能真正帮助你以这种方式组织事物。 我自己遇到了这个问题。 我的解决scheme是实现一个计时器。 计时器将每隔X秒轮询一次结果,当所有预期的结果被检索时,执行stream程可以继续。

 PollTimer扩展了Timer
 {
      public PollTimer()
      {
           //我已经设定每半秒轮询一次,但这可以是任何你想要的。
           //理想情况下,它只是客户端,所以你应该可以做到 
           //更频繁(在合理范围内),而不用担心performance太差
           scheduleRepeating(500);
      }
     公共无效运行 
      {
           //检查你的callback是否已经完成
          如果(未完成)
              返回; 
  //continue with execution flow ... } 

}

调用你的RPC,然后实例化一个新的PollTimer对象。 这应该够了吧。

GWT仿真不支持java.util.concurrent中的东西。 在这种情况下不会帮助你。 对于所有的意图和目的,您在客户端执行的所有代码都是单线程的。 尝试进入这个思维集。

理想情况下,您希望像其他海报一样,在一次asynchronous调用中尽可能地做到这一点。 有时你必须做一堆单独的电话。 就是这样:

你想链接asynchronous调用。 当最后的asynchronous完成(login)时,所有的项目都被加载。

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

干杯,

–Russ