how to defer a method call until the return of an async server call?

233 views
Skip to first unread message

Magnus

unread,
Jul 8, 2013, 11:55:17 AM7/8/13
to google-we...@googlegroups.com
Hi,

I use a form called "InvitationForm" to let the user create an invitation for a game, like this:

InvitationForm f = new InvitationForm();
...
f.setInviter(...);

In this case, the call to setInviter selects an entry in a list of users.
Unfortunately, the list of users is fetched by an asynchroneous server call, and the call might not have returned when setInviter is called.
Then, the setInviter method cannot do its work, because the list is still empty.

Of course, there are many ways to get along with this, e. g. storing the user identity in an additional class member variable within setInviter and doing the list selection when the server call returns.
I also used to use Scheduler.get().scheduleDeferred to get things done somewhat later, but in this case, it would not be guaranteed that the call has returned then.

Since this happens often, what would be an elegant way to do this?

Magnus

Jens

unread,
Jul 8, 2013, 12:30:02 PM7/8/13
to google-we...@googlegroups.com
Somewhat depends on the concrete case, there is probably not the one solution that always fits.

1.) Dependency Inject: Load the list externally, e.g. new InvitationForm(users). As setInviter() depends on the list its not a good idea to use invitationForm.setUsers() because then you still need to call them in the correct order.
2.) Observer pattern: Use EventBus or invitationForm.addUsersLoadedHandler() depending on the separation you want
3.) "Replay" method call: remember the fact that setInviter() has been called and do the actual work once the users list is loaded. This can be annoying to write if you have multiple methods that depend on the users list.
4.) Distinct load() method with callback: Introduce InvitationForm.loadUsers(new Callback<S, F> callback() {}); and call it externally. S is the type for onSuccess, F for onFailure. Both can be Void if you are not interested in them.
5.) Refactor code so that an inviter does not depend on the list of users he/she can invite. I would understand if the list of users depends on the inviter (because the users are friends or similar of the inviter) but the other way around sounds strange to me.

Personally I have never done 3.) so far.

I would always start with 5.) and then if the InvitationForm isn't widely used probably 1.). If the form is used in multiple areas I would stick to 2) or 4). In case of 2.) I would use EventBus if parties that are interested in this information might be "far away" from the InvitationForm. If only the parent is interested in this information the addUsersLoadedHandler() approach makes more sense to me.

-- J.

Magnus

unread,
Jul 9, 2013, 10:04:23 AM7/9/13
to google-we...@googlegroups.com
Hi Jens,

yes, there are many options, but as I wrote, there are always some annoying things that make the code unattractive somehow.

The options 2) and 3) require an additional member variable to hold the inviter's value until the server call returns, and finally you end up in redundancy: The same information about the inviter is hold in the member variable as well in the list widget itsef. The same holds for the invited user. In addition, this would simply move the synchronization task to another place.

The options 1) and 4) require an external intervention into the inner details of the class. A form object should accept and deliver the identities of the two players to its encapsulating context, but it should not reveal whether the users are selected from a list or if their names (or numbers) are entered in a text field.

The option 5) seems to result from a misunderstanding: The inviter and invited are identified by integers and independend from the list. But to display them in a list widget, it must be filled first.

However, there is not really a problem, but I would have appreciated if there were an option like this (inspired by Scheduler.get().scheduleDeferred):

public void setInviter (final int inviter)
{
 // we have access to the AsyncCallback<List<User>> object here

 Scheduler.get().scheduleDeferredWhenServerCallReturned
 (
  new Command()
  {
   public void execute ()
   {
    lst_Inviter.select(inviter);
   }
  }
}

The main benefit would be that we could pass the value to the schedule command and immediately forget it after that.

Magnus

Jens

unread,
Jul 9, 2013, 11:13:18 AM7/9/13
to google-we...@googlegroups.com
If inviter is a required information then make it a constructor arg:

private final AsyncCallback<List<User>> loadUsersCallback;
public InvitationForm(final int inviter) {
  loadUsersCallback = new AsyncCallback<List<User>>() {
    void onSuccess(final List<User> users) {
       fillList(users);
       select(inviter);
    }
  }
}

@Override
protected void onLoad() {
   super.onLoad();
   service.getUsers(loadUsersCallback);
}

That's what I would have done, but maybe I don't get your use case correctly ;-) So far I never really needed this scheduleDeferredWhenServerCallReturned feature, as I already have it.. its the onSuccess() method and I would rethink what I am doing until I can implement onSuccess() the way it should be.

-- J.

Ümit Seren

unread,
Jul 10, 2013, 10:41:14 AM7/10/13
to google-we...@googlegroups.com
I think this solution is the right one. 
In general these kind of scenarios would be a good fit for Futures/Promises especially if you depend on multiple asynchronous calls. 
https://code.google.com/p/gwt-async-future/
https://code.google.com/p/gwtquery/wiki/Promises

Jens

unread,
Jul 10, 2013, 12:28:54 PM7/10/13
to google-we...@googlegroups.com

In general these kind of scenarios would be a good fit for Futures/Promises especially if you depend on multiple asynchronous calls. 
https://code.google.com/p/gwt-async-future/

IMHO Futures don't buy you anything in GWT. In Java Future.get() blocks until the result is available, in GWT you just can't do that. Thus you still need a callback so that you know when the Future has its result filled. Otherwise you would need a Timer and periodically check if the Future is ready to read. At the end you probably write more code than you should (in addition to the code you have introduced through the library itself).

 
https://code.google.com/p/gwtquery/wiki/Promises

Looks a lot better but at the end its just a more declarative way of working with callbacks. I am not sure if this helps here. 
But because of the use of the Function interface it will probably read really well in IntelliJ and later helps a lot once GWT supports Java8 lambdas. I do something similar, see https://groups.google.com/d/msg/google-web-toolkit-contributors/UzESJTd_JxU/p9UbnWxlWQQJ



The key point is that Magnus wants to encapsulate asynchronous behavior but from the outside calls a synchronous method that depends on this encapsulated asynchronous behavior. IMHO the best way to solve this is to not be in that situation :-)

-- J.

Ümit Seren

unread,
Jul 10, 2013, 3:04:45 PM7/10/13
to google-we...@googlegroups.com
I think the big advantage of Promises/Futures is that you can pass a Future/Promise around.  It also makes handling multiple asynchronous easier than with callbacks (chaining vs nested callbacks). 
Of course they have to be non-blocking. 
Java8 and it's lambda constructs would make things much less verbose. 
Dart has a really nice concept of Futures and internally uses it wherever possible. 

But you are also right. The solution you suggested (selecting the item in the list in the onSuccess method is the way to go). 



-- J.

--
You received this message because you are subscribed to a topic in the Google Groups "Google Web Toolkit" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/google-web-toolkit/VYazDZ2ys3U/unsubscribe.
To unsubscribe from this group and all its topics, send an email to google-web-tool...@googlegroups.com.
To post to this group, send email to google-we...@googlegroups.com.
Visit this group at http://groups.google.com/group/google-web-toolkit.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Magnus

unread,
Jul 13, 2013, 9:09:57 AM7/13/13
to google-we...@googlegroups.com
Hi,

thanks to all.
Yes, using the onSuccess method is the way to go. But it requires additional member variables and - if there were more such cases - this could get a little bit ugly.
But it's ok for now.

Magnus
Reply all
Reply to author
Forward
0 new messages