Java)で複数の非同期コールバック関数が完了するのを待つ最良の方法は何ですか?特にAsyncCallbackでGWTを使用していますが、これは一般的な問題だと思います。今、確かにもっときれいな方法があります...
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
}
}
私のプロジェクトでは、この問題を解決する2つのクラスを作成しました。基本的に、個々のコールバックは親に登録されます。親は、各子コールバックが完了するのを待ってから、独自の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);
}
</ code>
私はそれを説明する投稿をここに書きました: GWTでの並列非同期呼び出し 。これらの2つのクラスの実装はその投稿からリンクされています(申し訳ありませんが、私は初心者ユーザーなので、ここではリンクを提供できません。複数のリンクを含めるにはカルマが足りません!)。
@Epsenが言うように、Future
はおそらくあなたが望むものです。残念ながら、Future
sがGWTと互換性があるとは思いません。 gwt-async-future プロジェクトは、この機能をGWTにもたらすと主張していますが、私は試したことはありません。一見の価値があるかもしれません。
私自身、これに苦労し、いくつかのメソッドを使用しました。「チェーン」メソッドは見苦しくなります(ただし、各メソッドのインラインクラスの代わりにクラスを作成すると改善できます)。
あなた自身のバージョンのバリアントは私にとってうまくいきます:
_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()
を呼び出します(これを行う場合を除き、失敗メソッドでもこれを実行してください)何か違います)
Readyメソッドでは、カウンターをデクリメントして、まだ未解決の呼び出しがあるかどうかを確認します。
それでも醜いですが、必要に応じて呼び出しを追加できます。
何よりもまず-このような状況に陥らないでください。 RPCサービスを再設計して、すべてのユーザーフロー/画面が機能するために最大で1つのRPC呼び出しを必要とするようにします。この場合、サーバーに対して3つの呼び出しを行っていますが、これは帯域幅の浪費です。レイテンシはアプリを強制終了するだけです。
ハックが不可能で本当に必要な場合は、 Timer を使用して、すべてのデータがダウンロードされたかどうかを定期的にポーリングします。上に貼り付けたコードassumeslogin()メソッドが最後に終了します-これは間違っています。最初に終了する可能性があり、その後、アプリは不確定な状態になります。これはデバッグが非常に困難です。
@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();
いくつかのアイデアを投げる:
コールバックは、HandlerManagerを使用してGwtEventを起動します。 readyメソッドを含むクラスは、コールバックメソッドによって発生したイベントのEventHandlerとしてHandlerManagerに登録され、状態(bookAPIAvailable、searchAPIAvailable、appLoaded)を保持します。
イベントが到着すると、特定の状態が変更され、すべての状態が希望どおりかどうかが確認されます。
GWTEvent、HandlerManager、およびEventHandlerの使用例については、 http://www.webspin.be/?p=5 を参照してください。
理想的には、他の投稿者が述べたように、1回の非同期呼び出しでできる限り多くのことを実行したいです。時々あなたはたくさんの別々の呼び出しをしなければならない。方法は次のとおりです。
非同期呼び出しをチェーンしたい。最後の非同期が完了する(ログイン)と、すべてのアイテムが読み込まれます。
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);
乾杯、
-ラス
Sriによると、最良のシナリオは、一度に1回だけバックエンドを呼び出すようにアプリを再設計することです。これにより、この種のシナリオが回避され、帯域幅と待ち時間が維持されます。 Webアプリでは、これは最も貴重なリソースです。
ただし、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
...
}
</ code>
}
RPCを呼び出してから、新しいPollTimerオブジェクトをインスタンス化します。これでうまくいくはずです。
Java.util.concurrentの内容は、GWTエミュレーションではサポートされていません。この場合は役に立ちません。すべての意図と目的のために、クライアント側で実行するすべてのコードはシングルスレッドです。そのマインドセットに入るようにしてください。