Manera limpia en GWT/Java para esperar a que finalicen múltiples eventos asincrónicos
evento focus en netbeans (8)
¿Cuál es la mejor manera de esperar que múltiples funciones de devolución de llamada asíncrona finalicen en Java antes de continuar? Específicamente, estoy usando GWT con AsyncCallback, pero creo que este es un problema genérico. Esto es lo que tengo ahora, pero seguramente hay una manera más limpia ...
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
}
}
Al igual que @Epsen, dice: Future
es probablemente lo que quieres. Desafortunadamente, no creo que Future
s sea compatible con GWT. El proyecto gwt-async-future afirma traer esta funcionalidad a GWT, aunque nunca lo he intentado. Puede valer la pena mirar.
El mejor escenario posible, como dijo sri, es rediseñar su aplicación para que solo llame al servidor de respaldo de a una por vez. Esto evita este tipo de escenario y conserva el ancho de banda y el tiempo de latencia. En una aplicación web, este es tu recurso más preciado.
Habiendo dicho eso, el modelo GWT RPC realmente no te ayuda a organizar las cosas de esta manera. Me he encontrado con este problema yo mismo. Mi solución fue implementar un temporizador. El temporizador sondeará los resultados cada X segundos, y cuando se recuperen todos los resultados esperados, el flujo de ejecución puede continuar.
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
...
}
}
Realiza tus llamadas a tu RPC y crea una instancia de un nuevo objeto PollTimer. Eso debería hacer el truco.
Las cosas en java.util.concurrent no son compatibles con GWT Emulation. No te ayudaré en este caso. Para todos los efectos, todo el código que hace en el lado del cliente es de un solo hilo. Intenta entrar en esa mentalidad.
En primer lugar, nunca te metas en tal situación. Rediseñe sus servicios de RPC de modo que cada flujo / pantalla de usuario requiera como mucho una única llamada de RPC para funcionar. En este caso, está haciendo tres llamadas al servidor, y es solo un desperdicio de ancho de banda. La latencia simplemente matará tu aplicación.
Si no puede y realmente necesita un truco, use un Timer para sondear periódicamente si se han descargado todos los datos. El código que pegó arriba supone que el método de inicio de sesión () será el último en finalizar, lo cual es incorrecto. Puede ser el primero en finalizar, y luego tu aplicación estará en un estado indeterminado, lo cual es muy difícil de depurar.
Escribí dos clases que resuelven este problema en mi proyecto. Básicamente, cada devolución de llamada individual se registra con un padre. El padre espera a que se complete la devolución de llamada de cada niño, y luego libera su propio handleSuccess ().
El código del cliente se ve así:
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);
}
Escribí una publicación que lo explica aquí: llamadas asincrónicas paralelas en GWT . La implementación de estas dos clases está vinculada desde esa publicación (lo siento, no puedo dar enlaces aquí porque soy un usuario novato, ¡no hay suficiente karma para incluir más de un enlace!).
He luchado con esto yo mismo, y he usado varios métodos: la ''cadena'' se pone fea (pero se puede mejorar si creas clases en lugar de clases en línea para cada método).
Una variante de tu propia versión funciona bien para mí:
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
}
Todo lo que hice fue crear un contador para el número de llamadas que voy a hacer, luego cada resultado de sincronización llama ready()
(asegúrese de hacer esto también en los métodos de falla, a menos que vaya a hacer algo diferente)
En el método listo, disminuyo el contador y veo si todavía hay llamadas pendientes.
Todavía es feo, pero te permite agregar llamadas según sea necesario.
Hice algo similar a @Sasquatch, pero en lugar de hacer uso de un objeto "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);
}
}
}
Luego en mi devolución de llamada solo llamo:
counter.count();
Lo ideal es que quieras hacer lo que otros carteles han establecido y hacer tanto como puedas en una sola llamada asincrónica. A veces tienes que hacer un montón de llamadas por separado. Así es cómo:
Desea encadenar las llamadas asincrónicas. Cuando finaliza la última sincronización (inicio de sesión), se cargan todos los elementos.
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);
Aclamaciones,
--Russ
Solo lanzando algunas ideas:
Las devoluciones de llamadas disparan algunos GwtEvent usando HandlerManager. La clase que contiene los métodos listos está registrada en el HandlerManager como un Manejador de eventos para los eventos activados por los métodos de devolución de llamada, y mantiene el estado (bookAPIAvailable, searchAPIAvailable, appLoaded).
Cuando llega un evento, se cambia ese estado específico y verificamos si todos los estados son los deseados.
Para ver un ejemplo con GWTEvent, HandlerManager y EventHandler, consulte http://www.webspin.be/?p=5