Подтвердить что ты не робот

Чистый путь в 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
    }
}
4b9b3361

Ответ 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