Friday, June 19th, 2009

Serial Asynchronous XmlHttpRequests

Category: Dojo, JavaScript

<p>By default we should always favour asynchronous XHR to help the responsiveness of our Web applications. However, have you ever wanted a way to serialize your XHR calls because you needed to do things in sequence as B relied on what came back from A?

You could call a synchronous Ajax call, but that locks up the browser. Thibaud Lopez Schneider has written up his thoughts here showing the difference between synchronous Ajax:

and async calls with magic to serialize them:

He then went and created a simple example code generator at asynchronous.me. It is interesting to look at the code of the serialized version, and see that it doesn’t do anything complex…. just passes in the next function to run as a callback:

javascript
< view plain text >
  1. function run() {  
  2.     request1(function () {  
  3.         request2(function () {  
  4.             request3(function () {  
  5.                 done();  
  6.             });  
  7.         });  
  8.     });  
  9. }  
  10. function request1(callback1) {  
  11.     // request 1  
  12.     print("request1");  
  13.     var xmlHttpRequest1 = new XMLHttpRequest();  
  14.     xmlHttpRequest1.open("GET", "something1?hello1", true);  
  15.     xmlHttpRequest1.onreadystatechange = function () {  
  16.         if (this.readyState == 4 && this.status == 200) {  
  17.             // response 1  
  18.             print("response1=" + this.responseText);  
  19.             // continue execution in the callback  
  20.             if (callback1) {  
  21.                 callback1();  
  22.             }  
  23.         }  
  24.     };  
  25.     xmlHttpRequest1.send();  
  26. }  
  27. function request2(callback2) {  
  28.     // request 2  
  29.     print("request2");  
  30.     var xmlHttpRequest2 = new XMLHttpRequest();  
  31.     xmlHttpRequest2.open("GET", "something2?hello2", true);  
  32.     xmlHttpRequest2.onreadystatechange = function () {  
  33.         if (this.readyState == 4 && this.status == 200) {  
  34.             // response 2  
  35.             print("response2=" + this.responseText);  
  36.             // continue execution in the callback  
  37.             if (callback2) {  
  38.                 callback2();  
  39.             }  
  40.         }  
  41.     };  
  42.     xmlHttpRequest2.send();  
  43. }  
  44. function request3(callback3) {  
  45.     // request 3  
  46.     print("request3");  
  47.     var xmlHttpRequest3 = new XMLHttpRequest();  
  48.     xmlHttpRequest3.open("GET", "something3?hello3", true);  
  49.     xmlHttpRequest3.onreadystatechange = function () {  
  50.         if (this.readyState == 4 && this.status == 200) {  
  51.             // response 3  
  52.             print("response3=" + this.responseText);  
  53.             // continue execution in the callback  
  54.             if (callback3) {  
  55.                 callback3();  
  56.             }  
  57.         }  
  58.     };  
  59.     xmlHttpRequest3.send();  
  60. }  
  61. function done() {  
  62.     // end  
  63.     print("done");  
  64. }

You can compare that approach to Dojo Deferred:

javascript
< view plain text >
  1. function run() {  
  2.         request1().addCallback(request2).addCallback(request3).addCallback(done);  
  3.     }  
  4.     function request1() {  
  5.         // request 1  
  6.         print("request1");  
  7.         var deferred = dojo.xhrGet({  
  8.             url: "something1?hello1",  
  9.             load: function (response) {  
  10.                 // response 1  
  11.                 print("response1=" + response);          
  12.             }  
  13.         });  
  14.         return deferred;  
  15.     }  
  16.     function request2() {  
  17.         // request 2  
  18.         print("request2");  
  19.         var deferred = dojo.xhrGet({  
  20.             url: "something2?hello2",  
  21.             load: function (response) {  
  22.                 // response 2  
  23.                 print("response2=" + response);  
  24.             }  
  25.         });  
  26.         return deferred;  
  27.     }  
  28.     function request3() {  
  29.         // request 3  
  30.         print("request3");  
  31.         var deferred = dojo.xhrGet({  
  32.             url: "something3?hello3",  
  33.             load: function (response) {  
  34.                 // response 3  
  35.                 print("response3=" + response);  
  36.             }  
  37.         });  
  38.         return deferred;  
  39.     }  
  40.     function done() {  
  41.         // end  
  42.         print("done");  
  43.     }

Related Content:

12 Comments »

Comments feed TrackBack URI

Continuations and coroutines FTW!

Comment by JChung2008 — June 19, 2009

A queue system would be much simpler and better, imho.

Comment by WebReflection — June 19, 2009

To have multiple Ajax Requests being executed serialized (still asynchronously though, think “queue” and not “synchronized”) is not only important, but it is also absolutely 100% imperative for many reasons. One simple example can be imagined when having two requests serializing the form and sending it back, then the first request removes or adds elements to the form in some manners… KABOOM…!
.
I have previously written extensively about the subject here; http://ra-ajax.org/how-to-create-an-ajax-library-part-5-wrapping-the-xmlhttprequest-xhr-object.blog
And here; http://ra-ajax.org/how-to-create-an-ajax-library-part-7-the-ra-ajax-class.blog
.
Also sometimes (though more seldom) you wish to execute XHR requests synchronously, one example is when you embed a JavaScript file into the page as a result of a callback. Then you wish to make sure that JS file is finished “executing” before you reference objects and types within it.
.
The first one is quite interesting (advanced) in implementation, but still not anymore advanced then that most serious Ajax Frameworks have had support for it for months, and often years. Like for instance http://ra-ajax.org – Disclaimer; I work with Ra-Ajax, remember…
.
To see this in action try to expand multiple items here; http://ra-ajax.org/samples/Ajax-Forum-Starter-Kit.aspx – *VERY* fast…
.
The second one is easier, though in fact fewer (possibly because of not that much of a demand) Ajax Frameworks really needs this possibility, which though when implemented nicely will seriously increase your maneuverability in regards to features and such in both your applications and your framework. Since then you can embed advanced widgets in Ajax Callbacks as response to some Business Logic happening on the server…
.
The first one though is probably the most underestimated things of all times in regards to Ajax, and also the most important feature *ANY* Ajax Framework can give you, though few does in fact …
.
I think it’s nice of Ajaxian to finally start focusing on this now though …
.
But the knowledge about these subjects have been out there for years…!
.
May I suggest a little bit more focus on frameworks, solutions, architecture and (framework-)design and a little bit less focus on DHTML and bling…?

Comment by ThomasHansen — June 19, 2009

What if I dont want to print out on paper? Seriously, print(‘done’)? :)

Comment by ChrisHeilmann — June 19, 2009

For those of us living in DWR-land, it’s as easy as:

dwr.engine.beginBatch();
// Multiple Ajax calls go here
dwr.engine.endBatch();

You get the callbacks firing in the order you made the Ajax calls (which of course, thanks to DWR, look just like JavaScript function calls) and as an added bonus, you get only a single request made to the server (although it could be hitting multiple objects on the server).

I’ve said it before and I’ll say it again: if you’re doing Java-based Ajax work and not using DWR, up the dosage my friend :)

Comment by fzammetti — June 19, 2009

“I’ve said it before and I’ll say it again: if you’re doing Java-based Ajax work and not using DWR, up the dosage my friend :)”

yeah, you’re the smart one out here. hopefully you’ve said enough and not find a need to say it again.

Comment by andrewwell — June 19, 2009

but is possiblle also using setTimeout with sync call ajax:

seTimeout(function() {
var res1=$.ajax({url:”http://1″,async:false});
var res2=$.ajax({url:”http://2″,async:false});
var res3=$.ajax({url:”http://3″,async:false});

},0);

Comment by sasuke — June 19, 2009

Hi Dion,

I stumbled on a library, called Flapjax, for reactive programming which makes this kind of pattern easy. The basic idea is that instead of using a callback model for coding reactive patterns, you chain functions on an event stream abstraction.

In this instance you would write something like this pseudo-code:
triggerEventStream.CallXHR(call1).CallXHR(call2).CallXHR(call3).
If you want to trigger it only once, you can have triggerEventStream be a stream with a single event.

I haven’t used that library yet, but the event stream abstraction seems really useful. It can also be used to handle reacting to UI events.

Here’s a good tutorial which explains how a naive Flapjax implementation might work: http://www.weaselhat.com/2007/08/11/lifting-in-flapjax/

Comment by Dumky — June 19, 2009

@andrewwell: That’s a pretty obnoxious reply on your part, don’t you think? Why the need for sarcasm there? If you’re not a fan of DWR, no problem… if you’re sick of me expressing how good a project I think it is, just ignore me… If you don’t agree with the sentiment, I’m all for a respectful discourse on the subject. But I see no point in being glib.

Comment by fzammetti — June 19, 2009

1. Synchronous XHR is not simply a bad idea; it’s a wildly unacceptable design that should be stricken from existence, and certainly never used. It’s fundamentally impossible to implement it within a browser under Javascript’s threading model (or lack thereof), in a way that doesn’t blow.

2. This is decidedly not a good idea for performance the way it’s described. If you need to make “multiple requests”, have all the necessary information at one point in time, but need to serialize them, then what you have is a single request. Otherwise, you’re just slowing everything down by creating unnecessary HTTP connections.

Comment by jgw — June 19, 2009

Vaadin (previously IT Mill Toolkit) also does this internally, although the implementation is a bit more complex than the simple example shown – as someone noted above, there are many cases to consider, e.g user clicks Button A and Button B in quick succession, but Button A removes Button B. You want to be as asynchronous as possible, but not asynchronousier ;-)

Comment by marc — June 22, 2009

that’s a good pattern to remember, anyway

Comment by alshur — July 20, 2009

Leave a comment

You must be logged in to post a comment.