39

複数の非同期コールバック関数が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
    }
}
4

8 に答える 8

41

プロジェクトでこの問題を解決する 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);
}

ここでそれを説明する投稿を書きました: Parallel Asynchronous Calls in GWT。これら 2 つのクラスの実装は、その投稿からリンクされています (申し訳ありませんが、私は初心者ユーザーであるため、ここにリンクを提供できません。複数のリンクを含めるには十分なカルマがありません!)。

于 2010-11-29T16:14:00.213 に答える
5

@Epsenが言うように、Futureおそらくあなたが望むものです。Future残念ながら、 s が GWT と互換性があるとは思えません。gwt -async-futureプロジェクトは、この機能を GWT にもたらすと主張していますが、私は試したことはありません。一見の価値があるかもしれません。

于 2010-06-07T21:09:25.673 に答える
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()(別のことを行う場合を除き、失敗メソッドでも必ずこれを実行してください)。

ready メソッドでは、カウンターをデクリメントし、まだ未処理の呼び出しがあるかどうかを確認します。

まだ見苦しいですが、必要に応じて呼び出しを追加できます。

于 2012-03-14T03:16:41.553 に答える
2

何よりもまず、そのような状況に陥らないでください。RPC サービスを再設計して、すべてのユーザー フロー/画面が動作するために必要な RPC 呼び出しが最大でも 1 つになるようにします。この場合、サーバーに対して 3 つの呼び出しを行うことになり、帯域幅の無駄遣いになります。レイテンシーはアプリを殺すだけです。

ハッキングが不可能で本当に必要な場合は、Timerを使用して、すべてのデータがダウンロードされたかどうかを定期的にポーリングします。上に貼り付けたコードは、login() メソッドが最後に終了することを前提としていますが、これは誤りです。それが最初に終了する可能性があり、アプリは不確定な状態になります。これはデバッグが非常に困難です。

于 2010-06-07T21:07:22.730 に答える
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();
于 2012-06-22T09:53:14.413 に答える
1

いくつかのアイデアを投げかけるだけです:

コールバックは、HandlerManager を使用して GwtEvent を起動します。Ready メソッドを含むクラスは、コールバック メソッドによって起動されるイベントの EventHandler として HandlerManager に登録され、状態 (bookAPIAvailable、searchAPIAvailable、a​​ppLoaded) を保持します。

イベントが到着すると、特定の状態が変更され、すべての状態が希望どおりかどうかを確認します。

GWTEvent、HandlerManager、および EventHandler を使用した例については、http://www.webspin.be/?p=5 を参照してください。

于 2010-06-12T22:02:05.917 に答える
0

理想的には、他のポスターが述べているように、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);

乾杯、

--ラス

于 2010-07-08T19:31:34.220 に答える
0

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

}

RPC を呼び出してから、新しい PollTimer オブジェクトをインスタンス化します。これでうまくいくはずです。

java.util.concurrent の内容は、GWT エミュレーションではサポートされていません。この場合、あなたを助けません。すべての意図と目的のために、クライアント側で実行するすべてのコードはシングル スレッドです。そのマインドセットに入るようにしてください。

于 2010-06-07T22:52:39.327 に答える