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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2993085/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 15:22:14  来源:igfitidea点击:

Clean way in GWT/Java to wait for multiple asynchronous events to finish

javagwtgoogle-ajax-api

提问by gerdemb

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

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

回答by Sripathi Krishnan

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.

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

If you can't and really need a hack, use a Timerto periodically poll if all data has downloaded. The code you pasted above assumeslogin() 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.

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

回答by Jason Hall

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

就像@Epsen 说的,Future可能就是你想要的。不幸的是,我不相信Futures 是 GWT 兼容的。在GWT-异步未来项目索赔将这一功能GWT,但我从来没有尝试过。这可能值得一看。

回答by Enmanuel Rivera

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.

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

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.

话虽如此,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
      ...
     }

}

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

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

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.

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

回答by Davy Meers

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

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

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

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

回答by Russ Sherk

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

——拉斯

回答by throp

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().

我在我的项目中编写了两个类来解决这个问题。基本上,每个单独的回调都向父级注册。父级等待每个子级回调完成,然后触发它自己的 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!).

我在这里写了一篇解释它的帖子: GWT 中的并行异步调用。这两个类的实现是从那个帖子链接的(对不起,不能在这里提供链接,因为我是一个新手用户 - 没有足够的业力来包含多个链接!)。

回答by Sasquatch

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)

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

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

在 ready 方法中,我递减计数器,看看是否还有未完成的调用。

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

它仍然很丑,但它允许您根据需要添加调用。

回答by DeadPassive

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

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

Then in my callback I just call:

然后在我的回调中,我只是调用:

counter.count();