Чистый путь в GWT/Java для ожидания завершения нескольких асинхронных событий
Каков наилучший способ дождаться завершения нескольких асинхронных функций обратного вызова на 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
}
}
Ответы
Ответ 1
Я написал два класса, которые решают эту проблему в моем проекте. В принципе, каждый отдельный обратный вызов регистрируется с родителем. Родитель ждет завершения каждого обратного вызова дочернего элемента, а затем отключает его собственный дескрипторSuccess().
Код клиента выглядит следующим образом:
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);
}
код > Я написал сообщение, объясняющее это здесь: Параллельные асинхронные вызовы в GWT. Реализация для этих двух классов связана с этой записью (извините, не могу дать ссылки здесь, потому что я новичок-пользователь - недостаточно кармы, чтобы включить более одной ссылки!).
Ответ 2
Как и @Epsen, Future
- это то, что вы хотите. К сожалению, я не верю, что Future
совместимы с GWT. Проект gwt-async-future претендует на включение этой функции в GWT, хотя я никогда не пробовал ее. Возможно, стоит посмотреть.
Ответ 3
Я сам боролся с этим, и я использовал несколько методов: "цепочка" просто становится уродливой (но может быть улучшена, если вы создаете классы вместо встроенных классов для каждого метода).
Вариант вашей собственной версии хорошо работает для меня:
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
}
Все, что я сделал, это создать счетчик для количества вызовов, которые я собираюсь сделать, затем каждый результат async вызывает ready()
(обязательно сделайте это и при ошибках, если вы не собираетесь что-то делать отличается)
В готовом методе я уменьшаю счетчик и вижу, остались ли еще невыполненные вызовы.
Он по-прежнему уродлив, но позволяет добавлять вызовы по мере необходимости.
Ответ 4
Прежде всего - никогда не попадайте в такую ситуацию. Перепроектируйте свои службы RPC таким образом, чтобы каждый пользовательский поток/экран требовал не более одного вызова RPC. В этом случае вы делаете три вызова на сервер, и это просто трата пропускной способности. Задержка просто убьет ваше приложение.
Если вы не можете и действительно нуждаетесь в взломе, используйте Timer для периодического опроса, если все данные загружены. Код, вставленный выше, предполагает, что метод login() будет последним для завершения - что неправильно. Его можно первыми завершить, а затем ваше приложение будет находиться в неопределенном состоянии, что очень сложно отладить.
Ответ 5
Просто подбросьте некоторые идеи:
Обратные вызовы запускают некоторый GwtEvent с помощью HandlerManager.
Класс, содержащий готовые методы, регистрируется в HandlerManager как EventHandler для событий, запускаемых методами обратного вызова, и содержит состояние (bookAPiavailable, searchAPiavailable, appLoaded).
Когда событие приходит, что определенное состояние изменяется, и мы проверяем, соответствуют ли все состояния.
В примере с использованием GWTEvent, HandlerManager и EventHandler см. http://www.webspin.be/?p=5
Ответ 6
Я сделал что-то похожее на @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();
Ответ 7
Лучший сценарий, как сказал Шри, заключается в том, чтобы перепроектировать ваше приложение, чтобы вызывать только бэкенд один раз за раз. Это позволяет избежать такого сценария и сохраняет полосу пропускания и время ожидания. В веб-приложении это самый ценный ресурс.
Сказав, что модель 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 Emulation. В этом случае не поможет. Для всех целей и целей весь код, который вы делаете на стороне клиента, является однопоточным. Попытайтесь войти в этот набор разума.
Ответ 8
В идеале вы хотите сделать то же, что и другие плакаты, и сделать столько, сколько сможете, в одном асинхронном вызове. Иногда вам приходится делать кучу отдельных вызовов. Вот как:
Вы хотите связать асинхронные вызовы. Когда последний async завершает (вход в систему), все элементы загружаются.
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);
Приветствия,
- Russ