在继续之前等待多个异步回调函数在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
}
}
答案 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);
干杯,
- 拉斯