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

发布于 2024-09-04 10:13:14 字数 816 浏览 1 评论 0原文

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

What is the best way to wait for multiple asynchronous callback functions to finish in Java before continuing. Specifically I'm using GWT with AsyncCallback, but I think this is a generic problem. Here's what I have now, but surely there is cleaner way...

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

人心善变 2024-09-11 10:13:15

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

I wrote two classes that solve this problem on my project. Basically, each individual callback registers with a parent. The parent waits for each child callback to complete, then fires off it's own handleSuccess().

The client code looks like this:

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

I wrote a post explaining it here: Parallel Asynchronous Calls in GWT. The implementation for these two classes is linked from that post (sorry, can't give links here because I'm a newbie user - not enough karma to include more than one link!).

青柠芒果 2024-09-11 10:13:15

就像 @Epsen 所说,Future 可能就是您想要的。不幸的是,我不相信 Future 与 GWT 兼容。不过,gwt-async-future 项目声称将此功能引入 GWT我从来没有尝试过。也许值得一看。

Like @Epsen says, Future is probably what you want. Unfortunately, I don't believe Futures are GWT-compatible. The gwt-async-future project claims to bring this functionality to GWT, though I've never tried it. It may be worth a look.

非要怀念 2024-09-11 10:13:15

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

您自己版本的一个变体对我来说效果很好:

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() (请务必执行失败方法上也是如此,除非您打算做一些不同的事情)

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

它仍然很丑陋,但它可以让您根据需要添加调用。

I've struggled with this myself, and I've used several methods- the 'chain' one just gets ugly (but can be improved if you create classes instead of inline classes for each method).

A variant of your own version works well for me:

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
}

All I did was create a counter for the number of calls I'm going to do, then each async result calls ready() (be sure to do this on the failure methods too, unless you're going to do something different)

In the ready method, I decrement the counter and see if there are still outstanding calls.

It's still ugly, but it lets you add calls as needed.

白龙吟 2024-09-11 10:13:15

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

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

First and foremost - don't ever get into such a situation. Redesign your RPC services such that every user flow/screen requires at most a single RPC call to work. In this case, you are making three calls to the server, and its just a waste of bandwidth. The latency will just kill your app.

If you can't and really need a hack, use a Timer to periodically poll if all data has downloaded. The code you pasted above assumes login() method will be the last to finish - which is wrong. Its may be the first to finish, and then your app will be in an indeterminate state - which is very difficult to debug.

大海や 2024-09-11 10:13:15

只是提出一些想法:

回调使用 HandlerManager 触发一些 GwtEvent。
包含就绪方法的类在 HandlerManager 中注册为回调方法触发的事件的 EventHandler,并保存状态(bookAPIAvailable、searchAPIAvailable、a​​ppLoaded)。

当事件到达时,特定状态会发生更改,我们检查所有状态是否都符合预期。

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

Just tossing up some ideas:

The callbacks fire some GwtEvent using the HandlerManager.
The class containing the ready methods is registered with the HandlerManager as an EventHandler for the events fired by the callback methods, and holds the state (bookAPIAvailable, searchAPIAvailable, appLoaded).

When a event arrives that specific state is changed, and we check if all the states are as desired.

For an example using the GWTEvent, HandlerManager and EventHandler, see http://www.webspin.be/?p=5

锦爱 2024-09-11 10:13:15

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

I did something similar to @Sasquatch, but instead making use of a "CallbackCounter" object:

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

Then in my callback I just call:

counter.count();

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

Best case scenario, as sri said, is to redesign your app to only call the backend once at a time. This avoids this kind of scenario, and preserves bandwidth and latency time. In a web app, this is your most precious resource.

Having said that the GWT RPC model doesn't really help you to organize things in this manner. I've run into this problem myself. My solution was to implement a timer. The timer will poll your results every X seconds, and when all your expected results are retrieved, your execution flow can continue.

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

}

Make your calls to your RPC, then instantiate a new PollTimer object. That should do the trick.

The stuff in java.util.concurrent is not supported by GWT Emulation. Wont help you in this case. For all intents and purposes, all of the code you do on the client side is single threaded. Try to get into that mind set.

ペ泪落弦音 2024-09-11 10:13:15

理想情况下,您希望按照其他发帖者所述进行操作,并在单个异步调用中尽可能多地执行操作。有时您必须进行一系列单独的调用。方法如下:

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

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

干杯,

--拉斯

Ideally, you want to do as other posters have stated and do as much as you can in a single async call. Sometimes you have to do a bunch of separate calls. Here's how:

You want to chain the async calls. When the last async completes (login), all the items are loaded.

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

Cheers,

--Russ

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文