在GWT / Java中清理方式以等待多个异步事件完成

时间:2010-06-07 20:55:17

标签: java gwt google-ajax-api

在继续之前等待多个异步回调函数在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
    }
}

8 个答案:

答案 0 :(得分:42)

我写了两个类来解决我的项目中的这个问题。基本上,每个单独的回调都向父母注册。父级等待每个子回调完成,然后触发它自己的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。这两个类的实现是从该帖子链接的(抱歉,这里不能给出链接,因为我是新手用户 - 没有足够的业力来包含多个链接!)。

答案 1 :(得分:5)

就像@Epsen所说,Future可能就是你想要的。不幸的是,我不认为Future与GWT兼容。 gwt-async-future项目声称将此功能引入GWT,但我从未尝试过。值得一看。

答案 2 :(得分:4)

我自己也在努力解决这个问题,而且我使用了几种方法 - '链'只是变得丑陋(但如果为每种方法创建类而不是内联类,可以改进)。

您自己版本的变体适合我:

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()(确保在失败方法上执行此操作,除非您打算做一些不同的事情)

在就绪方法中,我递减计数器并查看是否还有未完成的呼叫。

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

答案 3 :(得分:2)

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

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

答案 4 :(得分:1)

只是抛出一些想法:

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

当事件到达时,特定状态发生变化,我们检查所有状态是否符合要求。

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

答案 5 :(得分:1)

我做了类似于@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);
        }
    }
}

然后在我的回调中,我只是打电话:

counter.count();

答案 6 :(得分:0)

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

说过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 Emulation不支持java.util.concurrent中的内容。在这种情况下不会帮助你。出于所有意图和目的,您在客户端执行的所有代码都是单线程的。试着进入那种心态。

答案 7 :(得分:0)

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

您想要链接异步调用。当最后一个异步完成(登录)时,所有项都被加载。

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

干杯,

- 拉斯