Что лучший способ состоит в том, чтобы ожидать нескольких асинхронных функций обратного вызова для окончания в 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
}
}
Первое и самое главное - никогда не попадайте в такую ситуацию. Перепроектируйте свои RPC сервисы так, чтобы каждый пользовательский поток/экран требовал не более одного RPC вызова для работы. В этом случае вы делаете три вызова к серверу, и это просто пустая трата пропускной способности. Задержка просто убьет ваше приложение.
Если вы не можете и вам действительно нужен хак, используйте Timer для периодического опроса, все ли данные загружены. Код, который вы вставили выше, предполагает, что метод login() будет завершен последним - что неверно. Он может завершиться первым, и тогда ваше приложение будет находиться в неопределенном состоянии, что очень трудно отлаживать.
Лучший сценарий, как сказал Шри, это перепроектировать ваше приложение так, чтобы оно вызывало бэкенд только один раз за раз. Это позволит избежать подобного сценария и сохранить пропускную способность и время задержки. В веб-приложении это самый ценный ресурс.
Однако модель GWT RPC не очень-то помогает вам организовать все таким образом. Я сам столкнулся с этой проблемой. Моим решением была реализация таймера. Таймер будет опрашивать ваши результаты каждые X секунд, и когда все ожидаемые результаты будут получены, ваш поток выполнения может быть продолжен.
PollTimer расширяет Timer
{
public PollTimer()
{
//Я установил опрос каждые полсекунды, но это может быть все, что вы захотите.
//В идеале это будет только на стороне клиента, так что вы сможете делать это...
//более частым (в пределах разумного), не слишком беспокоясь о производительности
scheduleRepeating(500);
}
public void run
{
//проверяем, все ли ваши обратные вызовы были выполнены
if (notFinished)
return;
//continue with execution flow
...
}
}
Выполните вызовы RPC, затем создайте новый объект PollTimer. Это должно помочь.
Материал в java.util.concurrent не поддерживается GWT Emulation. Это не поможет вам в данном случае. Для всех намерений и целей, весь код, который вы делаете на стороне клиента, является однопоточным. Постарайтесь принять этот образ мыслей.
Как говорит @Epsen, Future
- это, вероятно, то, что вам нужно. К сожалению, я не верю, что Future
совместимы с GWT. Проект gwt-async-future претендует на то, чтобы привнести эту функциональность в GWT, хотя я никогда его не пробовал. Возможно, стоит взглянуть.
Просто подбрасываю несколько идей:
Обратные вызовы вызывают некоторое GwtEvent, используя HandlerManager. Класс, содержащий готовые методы, регистрируется в HandlerManager как EventHandler для событий, запускаемых методами обратного вызова, и хранит состояние (bookAPIAvailable, searchAPIAvailable, appLoaded).
Когда приходит событие, это конкретное состояние изменяется, и мы проверяем, все ли состояния соответствуют желаемым.
Пример использования GWTEvent, HandlerManager и EventHandler смотрите в http://www.webspin.be/?p=5
В идеале, вы хотите сделать так, как говорили другие плакаты, и сделать как можно больше в одном асинхронном вызове. Иногда приходится выполнять несколько отдельных вызовов. Вот как:
Вы хотите выстроить цепочку вызовов async. Когда последний 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);
Cheers,
--Russ