No, we don't do synchronous

Feb 12 2007

There are a few questions I see repeatedly on the GWT developers list, and one of the most common seems to be why in the world we were so clueless as to forget to support synchronous XMLHttpRequest. Ok, most of the people asking don't call us clueless; they simply ask why the feature is excluded. But the question has been asked so often that I feel inclined to answer it somewhere easily linkable, namely here.

The truth is that synchronous XMLHttpRequest is evil, and we don't do evil. The reason I say it's evil it because it's one of those classic examples of a technique that makes developers feel a little better at the expense of the users. For instance, one common case that often prompts a plea for a synchronous mechanism is the need to load resources in order. And I won't argue at all that the asynchronous method isn't more complex for the developer:

HttpRequest.asyncGet("/url-a",new ResponseTextHandler() { public void onCompletion(String value) { HttpRequest.asyncGet("/url-b", new ResponseTextHandler() { public void onCompletion(String value) { // finally have both url-a and url-b loaded. } }); } });

Whereas, the synchronous way looks pleasantly concise:

String a = (String)HttpRequest.syncGet("/url-a"); String b = (String)HttpRequest.syncGet("/url-b");

There's only one wrinkle. When you decide to go with the synchronous method, you tell the browser not to do anything else until your XMLHttpRequest has finished. And by anything, I mean no stop button, no back button, no switching to another tab, and in some cases no keeping the screen painted appropriately. In other words, the browser momentarily enters into a non-working state. This is the same state that reports as "Not responding" in your favorite process monitor; that alone will hopefully convince you that calling it a non-working state is not just empty hyperbole.

"Yes, but when you know that the data you are fetching is very small, this is safe."

Not true. Well, if you could make some universal guarantees about the minimum speed and reliability of every network in the world maybe it would be true. But that seems like a much more difficult task than handling asynchronous callbacks. Networks are inherently unreliable. In fact, look at the very design of the protocols for sending data and you'll see that the people who had the least faith in networks were the people actually designing them. And not a day goes by that I don't end up in some half broken state on a wireless network. As an experiment, go to slashdot or digg and try to click on all the links on the front page. On the three or four that you give up on after ten seconds of waiting for the server to respond, consider how you would feel if the back button had been unavailable when you tried to go back. That's pretty much the user experience you endorse when you opt for synchronous XMLHttpRequest.

The underlying reason this happens is fairly straight forward. The javascript engine within browsers only ever sees a single thread and it's the UI thread, which is drastically limited in its ability to engage in long running computations since it has to be available for user interface events. In fact, if you want to provide adequate user experience, you should never do any continuous computation that takes longer than 100ms. Why? That same thread is also responsible for visually updating the application and the human eye regards updates that are separated by less than 100ms to be unquestionably instantaneous. Additionally if there is any movement taking place in the user interface, failing to update the interface in time can destroy perceptual fusion. That's simply the phenomenon that allows us to perceive a rapid sequence of discrete frames as a continuous progression of time. Have you ever tried to use the scrollbar on a application that is responding sluggishly? It's nearly impossible.

And finally, here's a demonstration. Click on the two buttons below to compare the worst case of user experience using both synchronous and asynchronous methods. I think you'll agree that asynchronous is worth the pain of a little extra code. You might even agree that synchronous is evil. We certainly think it is, so it won't be in GWT, since we don't do evil.

javascript!? something is broken.

Note: It is important to point out that what you experience when you click the Synchronous button above is very different in each of the major browsers.

Firefox & IE6

Firefox and IE6 do about the worst thing imaginable. They completely lock up the UI event loop. You can't move the window, repaints are not handled; the browser is effectively inoperable until the call completes. Sadly for Firefox, this is a vast improvement over what it once did. There was a time when it would lock up and enter a very tight wait loop that pegged the CPU. At least in the most recent versions, your CPU is free to launch another browser and read reddit while you wait on the original browser to return to life.

Opera

Opera tries really hard to work around the issues related to synchronous XHR. If you have Opera installed, it is well worth giving it a try. The Opera folks don't lock the browser. In fact, they don't really lock anything during a synchronous call. You can switch tabs and hit the back button, the chrome of the browser continues to function. But for events in the page itself, they simply don't deliver the related events until the synchronous call completes. The user can click and scroll all they want during the call, but no event handlers will get called until the event completes. If everyone used Opera, GWT might actually support synchronous calls.

Safari/WebKit

Safari sits somewhere in between Firefox and Opera in terms of user experience. When you click the Synchronous button above, the back button will be locked, the page will not scroll, but you can drag the window around. So at least, you can move the unresponsive window off to the side.

Update: I started writing this last month and let it sit idle in my proof-reading queue for a while. In that time Mark Pruett published “Do Sync Calls Freeze Browsers?” and I can't help but notice that he reports that IE6 does not freeze on synchronous calls. I don't think that is completely accurate. The immediate window making the call is completely frozen. Other IE windows are still functional, but that doesn't really help the situation much at all and it is a completely different user experience from what you get with Opera. I just wanted to make that clear because a quick read of his article might lead some to believe that it is suitable to do synchronous calls on IE6.