Wait for all asynchronous calls to finish

3,116 views
Skip to first unread message

AirJ

unread,
Apr 27, 2009, 8:33:33 PM4/27/09
to Google Web Toolkit, di....@gmail.com
Hi GWT gurus,

I have a generic questions regarding asynchronous calls. Basically in
our gwt web application, when user clicks on "save button", the
application launches a bunch of asynchronous calls(validation, server
side callbacks).

"Save" should only be invoked when all those asynchronous calls are
processed.

One way to do it is to call "Save" in the onCallbackSuccess() method
of those asynchronous calls. But it will make the code very
unstructured and hard to trace.

Essentially I need a method to flush all the queued asynchronous calls
before I make a new one. Will
DeferredCommand.addCommand() solve the problem?

Thanks,
Di

Vitali Lovich

unread,
Apr 27, 2009, 9:41:44 PM4/27/09
to Google-We...@googlegroups.com, di....@gmail.com
No.  "Waiting" in the browser means it freezes it (remember - single threaded).  Why not just disable the input fields that can lead to an RPC call (& some kind of processing indicator so the user understands what's going on) & reenable them when the call completes.

Just apply a proper MVC pattern & you'r code should be clean (i.e. your controller will disable the view when the model is busy updating the backend & reenable it when the model indicates it is finished).

AirJ

unread,
Apr 28, 2009, 12:55:29 AM4/28/09
to Google Web Toolkit
Sorry, "Wait" may have a misnomer here. I don't mean to have browser
block waiting for the asynchronous calls to finish.
What I mean is, before launching another asynchronous call, flush out
the AsyncCallbacks in the stack.

In a complicated UI screen, when saving the record, each field may go
through different validation asynchronous calls. Saving the record
without waiting for the previous asynchronous calls to finish will
result in unpredictible behavior. For example, validation may change
some record value. If save is invoked before validations are finished,
old values may be saved.

I thought DeferredCommand.addCommand() would serve the purpose. From
the javadoc
Enqueues a Command to be fired after all current events have been
handled. Note that the Command should not perform any blocking
operations.

I am wondering if the "events" include asynchronous calls, or are they
just referring to DOM events.

On Apr 27, 6:41 pm, Vitali Lovich <vlov...@gmail.com> wrote:
> No.  "Waiting" in the browser means it freezes it (remember - single
> threaded).  Why not just disable the input fields that can lead to an RPC
> call (& some kind of processing indicator so the user understands what's
> going on) & reenable them when the call completes.
>
> Just apply a proper MVC pattern & you'r code should be clean (i.e. your
> controller will disable the view when the model is busy updating the backend
> & reenable it when the model indicates it is finished).
>

Vitali Lovich

unread,
Apr 28, 2009, 2:11:02 AM4/28/09
to Google-We...@googlegroups.com
No just DOM events.  Like I said, just disable the UI screen (i.e. popup a modal dialogbox) until the RPC completes one way or another.  That's by far the simplest solution I can think of.

More complicated ones would involve grouping forms & specifying which are incompatible.  Then a framework would take care of disabling input on conflicting forms that have pending RPC.  This would allow the user to do as much as possible without having to potentially wait for the server.

You can always just ignore RPC calls

if (someStaticGlobalVariable < allowedNumberOfRPCCalls) {
   someStaticGlobalVariable++;
   myService.rpcCall(new AsyncCallback() {
      onFailure() { someStaticGlobalVariable--; }
      onSuccess() { someStaticGlobalVariable--; }
   });
}

the problem with that approach though is that if you don't disable user input, the user will get frustrated wondering why they're clicking but the web-app isn't doing anything.  So disabling gives the user an indication of what is going on & ensures that multiple RPC calls don't happen.

At the very least, just disable the widget that initiates the RPC action (if you want to allow the user to continue editing data while the RPC is in progress).

Jon Denly

unread,
Apr 28, 2009, 2:17:48 AM4/28/09
to Google Web Toolkit
It might be useful is to use a callbacks registry (perhaps the page
itself) that callbacks register themselves with when they start and
remove themselves when they return (successfully or otherwise). By
extending AsyncCallback you can make this process transparent. As
part of the remove process, if there are no outstanding callbacks
still running, another event can be triggered. In your case, your
validation callbacks would register themselves when they are setup and
deregister themselves when they return. The last one to deregister
itself would then trigger the save.

Hope this helps,

Jon

Jason Essington

unread,
Apr 28, 2009, 10:43:18 AM4/28/09
to Google-We...@googlegroups.com
So, you click save, then each field is validated individually via an
RPC?

One trick is to chain your RPC Calls. I have an abstract Callback that
implements Command and also has a field to hold an additional Command.
CommandCallback the execute() method is able to Fire the RPC that is
handled by the callback, and after onSuccess() is processed any
command added to the callback is executed (another commandCallback for
instance) that way you can create a whole chain of RPCs and call
execute on the first and each will be sent in sequence.

to prevent the save button from being pressed again while these RPCs
are executing you can disable the button, and enable it again as a
final command in the chain.

Of course, if you can combine all of these RPCs into a single request
you'll have better performance.

-jason

AirJ

unread,
Apr 28, 2009, 2:44:29 PM4/28/09
to Google Web Toolkit
Thanks for all the help.

Vitali, I cannot simply disable the "save" button since our field
validation happens when field loses focus. If I disable "Save" button,
user will be confused.

Jason, yes, I do have a similar AsyncCommand package that does the
command chaining. The problem is, our UI is driven by metadata. The
logic is prettty complicated at the client side. Even chaining
commands are making the code convoluted.

Denly, thanks for the suggestion. I finally decided to follow your
advice and implemented our own "flush" mechanism. It works perfectly!

Vitali Lovich

unread,
Apr 28, 2009, 3:32:18 PM4/28/09
to Google-We...@googlegroups.com
Here's my approach when designing something like that.  Any validation should happen immediately client side since that is one of the major benefits of using AJAX.  Doing an RPC call every time a field loses focus is pretty inefficient - I think doing it this way is too easy to make it a crappy experience for your users (now if the validation needs to access some kind of resource that isn't available from the sandbox, that's a different story, although that doesn't seem like a validation that should happen instantly).

In fact, if you do it right, you can share most (if not all) the validation code between client side & server side.  Then when the user presses save, the data is sent off to be saved at which points it gets actually validated (once) on the server.

This approach has the following benefit:
Lower server load - you're not making multiple RPC calls to process 1 form.
Lower client load - you're not making multiple RPC calls to process 1 form
Better user experience - validation happens instantly instead of requiring a round trip to the server.  Also, what happens if the server happens to be down at that particular moment?  Handling the failure of 1 RPC point is much easier conceptually than multiple.

Remember - RPC calls cost in terms of processing power (2 times serialization & deserialization) & bandwidth.

To top it all off, if this is a form that has some kind of save button anyways, you are going to double the amount of validation you need to do - once when the user enters the data & all over again when they hit save.  So the architecture for your program is not taking advantage of what Javascript offers which is to offload work that the client can do.

Anything & everything that can be done by the user, should be done with the server doing minimal work & being stateless.  The server should, IMHO, be a dumb machine that just processes requests & responses immediately.  All state goes to the client, persistance goes to the database.  The validation is a small little layer of business logic that sits between the servlet & persistance layers to ensure valid data, permissions, etc.  If any state information is needed for the server, then the client is responsible for supplying it (of course you need to be aware of validating this too - depending on the application, you may want to keep track of the state in the DB.  In all my apps I've been able to architecture it so the state was not important for the server, but that isn't saying much).
Reply all
Reply to author
Forward
0 new messages