Manière propre en GWT / Java pour attendre la fin de plusieurs événements asynchrones
Quelle est la meilleure façon d'attendre que plusieurs fonctions de rappel asynchrones se terminent en Java avant de continuer. Spécifiquement, J'utilise GWT avec AsyncCallback, mais je pense que c'est un problème Générique. Voilà ce que j'ai maintenant, mais il y a sûrement un moyen plus propre...
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
}
}
8 réponses
j'ai écrit deux cours qui résolvent ce problème sur mon projet. En gros, chaque appel individuel s'enregistre avec un parent. Le parent attend que chaque appel d'enfant soit terminé, puis déclenche son propre handleSuccess ().
le code client ressemble à ceci:
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);
}
j'ai écrit un post en expliquant ici: appels asynchrones parallèles en GWT. L'implémentation de ces deux classes est liée de ce post (désolé, ne peut pas donner des liens ici parce que je suis un internaute novice - pas assez de karma pour inclure plus d'un lien!).
Comme @Epsen dit, Future
est probablement ce que vous voulez. Malheureusement, je ne crois pas Future
s sont compatibles avec GWT. gwt-async-avenir le projet prétend apporter cette fonctionnalité à GWT, bien que je ne l'ai jamais essayé. Il peut être en valeur un regard.
j'ai moi - même eu du mal à le faire, et j'ai utilisé plusieurs méthodes-celle de la "chaîne" devient juste moche (mais peut être améliorée si vous créez des classes au lieu des classes inline pour chaque méthode).
une variante de votre propre version fonctionne bien pour moi:
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
}
Tout ce que j'ai fait c'est créer un compteur pour le nombre d'appels que je vais faire, puis chaque résultat async appels ready()
(assurez-vous de faire cela sur les méthodes d'échec aussi, sauf si vous allez faire quelque chose les différentes)
dans la méthode ready, Je décrémente le compteur et vois s'il y a encore des appels en cours.
c'est encore moche, mais ça permet d'ajouter des appels au besoin.
d'Abord et avant tout - ne jamais entrer dans une telle situation. Modifiez la conception de vos services RPC de façon à ce que chaque flux/écran de l'utilisateur ne nécessite au plus qu'un seul appel RPC pour fonctionner. Dans ce cas, vous faites trois appels vers le serveur, et c'est juste un gaspillage de bande passante. La latence va juste tuer votre application.
Si vous ne pouvez pas vraiment et ont besoin d'un hack, utiliser un Timer pour interroger périodiquement si toutes les données ont été téléchargées. Le code que vous avez collé au-dessus!--5-->suppose connexion() la méthode sera le dernier à finir - ce qui est faux. Sa peut être le premier à terminer, puis votre application sera dans un état indéterminé - ce qui est très difficile à déboguer.
Simplement jeter quelques idées:
les callbacks tirent un peu de GwtEvent en utilisant le HandlerManager. La classe contenant les méthodes ready est enregistrée avec le HandlerManager comme un EventHandler pour les événements déclenchés par les méthodes callback, et détient l'état (bookapiavable, searchapiavable, appLoaded).
Quand un événement arrive l'état est changé, et nous vérifions si tous les états sont comme souhaité.
pour un exemple utilisant le GWTEvent, HandlerManager et EventHandler, voir http://www.webspin.be/?p=5
j'ai fait quelque chose de similaire à @Sasquatch, mais à la place j'ai utilisé un objet "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);
}
}
}
Puis dans mon rappel je viens de l'appeler:
counter.count();
le meilleur scénario, comme l'a dit sri, est de redessiner votre application pour ne rappeler l'arrière-plan qu'une fois à la fois. Cela évite ce genre de scénario, et préserve la bande passante et le temps de latence. Dans une application web, c'est votre ressource la plus précieuse.
avoir dit que le modèle GWT RPC ne vous aide pas vraiment à organiser les choses de cette manière. J'ai ce problème moi-même. Ma solution était de mettre en place un minuteur. La minuterie affichera vos résultats toutes les X secondes, et quand toutes vos attentes les résultats sont récupérés, votre flux d'exécution peut se poursuivre.
//continue with execution flow
...
}
}
faites vos appels à votre RPC, puis instanciez un nouvel objet PollTimer. Cela devrait faire l'affaire.
Les trucs en java.util.concurrent n'est pas supporté par L'émulation GWT. Je ne vous aiderai pas dans cette affaire. À toutes fins pratiques, tout le code que vous faites du côté du client est un simple threaded. Essayez d'entrer dans cet état d'esprit.
Idéalement, vous voulez faire comme les autres affiches ont déclaré et faire autant que vous pouvez en un seul appel asynchrone. Parfois, il faut faire des appels séparés. Voici comment faire:
vous voulez enchaîner les appels asynchrones. Lorsque le dernier async termine (login), tous les éléments sont chargés.
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);
santé,
-- Russ