kellegous.com

 

Article

No, we don't do synchronous

February 12th 2007 late at night

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.

trackbacks / pings

Pings are currently disabled; they will be accepted very soon.

comments

#1
David

I found this article surprisingly entertaining and it will definitely end up being forwarded to my boss ;)

#2
ultraklon

Thanks for explaining! I accept everything that is justified. This is accepted

#3
Daniel Michaeloff

I found this looking for why Safari was locking up hard on a synchronous request, and sure enough, the button above sends it meditating with a spinny cursor forever.

Safari version 2.0.3 on a Mac Mini running OSX 10.4.6.

The asynch button works on Safari, but get a script error in IE7.

#4
kellegous

thanks daniel.

i’ll find an ie7 install and figure out what the issue is.

#5
kellegous

IM N UR IPHONE POSTN COMMENTZ

#6
David Masover

Yes, I do know how to create paragraphs. Unfortunately, it looks like your comment system doesn't. (In the preview, anyway, this appears as one gigantic unbroken line of text.)

First, to me, "synchronous" doesn't mean "freeze the browser" -- that would be a broken implementation.

To me, "synchronous" means "block the script that called send()", which seems much more reasonable. After all, we do plenty of other things synchronously in scripts -- from arithmetic to generating HTML elements to prompting the user for information -- without preventing them from navigating away or switching to another tab. Why not a synchronous XHR?

Anything can be abused, and that it can be abused is apparently no reason to just arbitrarily leave it out. The blink tag comes to mind -- it doesn't seem to have any purpose other than abuse, but Firefox still supports it (as of 2.0.0.4). Javascript alerts and prompts both could be implemented better, except in development, and they do block the user from using their browser -- as I understand it, you can still DOS someone by doing while(true){alert('hi')}. But synchronous requests actually do have a valid purpose.

For example: I've got a page which has an init() function called onLoad -- probably a common thing, right? The init function is in a separate file, and among other things, it wants to resize things -- it calls resize(), also called by onResize. So, in a third file, I have some code to detect the size of the scrollbar.

Ideally, I'd like my script to be able to load this third file by itself, rather than have it be part of the page. It seemed simple enough:

var http = new XMLHttpRequest();
http.open('GET', 'foo.js', true);
http.send(null);
eval(http.responseText);

I'd even made that into a loadFile(url) function. But thanks to zealots like you, it doesn't work.

Can't work well asynchronously, that I know of. Only real alternative is to either add another <script src...> to every html file that includes this one, or have my script generate more script tags -- which may or may not have the same problem that asynchronous does.

And this file is needed to be able to resize that page -- about the only advantage asynchronousness has here is if I wanted to display a big "loading" message until that file is loaded.

#7
kellegous

David,

Sorry about the paragraph breaks, if you use any html whatsoever in your comments, I don’t do any conversion for you.

Synchronous, as you are describing, does mean block the caller. However, in browsers, the caller is the UI event thread. So, in effect, synchronous does mean freeze the browser and most importantly it means that you are responsible for yielding control in a reasonable amount of time. BTW, this is not at all an uncommon architecture. If you were to write a desktop application you would have to contend with this there as well.

The work done on a UI event thread should be restricted to tasks that are short-lived and deterministic. Networking is inherently non-deterministic. This is not a matter of abuse. Doing synchronous XHR is truly flawed. Ironically, most of the user complaints that I’ve heard stemmed from the exact use case you describe. There was an earlier version of a popular Ajax library that loaded all modules via synchronous xhr. Users complained that many pages built with that library would hang on page load and render the back button unusable.

As to the assertion that my kind is somehow responsible for your code snippet not working: I’ll just tell you it has nothing at all to do with zealotry, neither by your meaning of the word nor by its correct meaning. To put it in your own terms, the call you make does not “block the script that called send()” since you pass true for the asyncFlag parameter in open. Of course, I don’t see any reason why you can’t do this asynchronously, so the best thing to do would be to simply add an onreadystatechange handler to your request object and eval the responseText when the call completes.

#8
Marek Jakubczyk

OK, No synchronous calls. We should live with this. But how to solve this problem:

if(someInstance.check()) { ... }

where check() method checks something on the server and returns boolean value. Let’s say that because of security reasons, I can’t get data to the client to check them offline. Do you have any idea how to resolve this problem having asynchronous calls only?

#9
JC Bagley

My situation for needing synchronous calls is exactly what Marek describes. I need to validate the data that the user has entered, the validation must take place on the web server, and I specifically do not want the user to do anything else until the validation completes. That’s a synchronous action.

I don’t care how the synchronous call is implemented (syncGet() or asyncGet() with some fancy callback wrappers in place that allow for UI refreshes, back button to be pressed, but speficially denies the user to do anything else in the form).

I think this article focuses too much on “why doesn’t GWT make use of syncGet()” instead of the real underlying question “how can we have a GWT request block until the server has fulfilled the request?”

#10
pohl

JC Bagley:

Wouldn’t it be better to phrase the question “How can I make sure that action A will not happen until conditions X, Y, and Z have been met?”

The reason that I suggest this is that blocking the lone, solitary thread that you have at your disposal will produce the behavior shown by the button above.

If you allow things to happen asynchronously, but check conditions prior to doing something you want to protect, you can get the validation behavior that you desire without a synchronous call to the server.

#11
kellegous

@Marek – You simply have to restructure the call so that your return value can be delivered asynchronously,

// update the ui.
instance.check(function(returnValue) {
  if (returnValue) {
    // user has permission, update ui accordingly.
  } else {
    // user does not have permission, update ui accordingly.
  }
});

@JC Bagley – The reason why the article focuses on why GWT doesn’t provide access to synchronous XHR is because that is precisely what it is about. I wouldn’t write an article about “how we can have a GWT request block until the server has fulfilled the request,” because I really don’t think it’s a useful thing to accomplish. Others have pondered ways to simulate continuations and other means of freezing the call stack, but they tend to just lead to complex solutions that produce bad user experience. Personally, I would rather work asynchronously in these cases.

Post a Comment: To leave a new comment, simply type your message below. Markup is also allowed as long as it conforms to XHTML Strict. A list of allowable tags is available in the Comment Guidelines. Obviously, if the words “XHTML Strict” mean nothing to you, you should stick with just typing your message below.

about kellegous.com

kellegous.com is the personal site of kelly norton, a designer and engineer living in Atlanta, Georgia. Kelly used to be a graduate student at the MIT Media Lab but graduated in the summer of 2006. Before that, he was the Senior VP of Technology Development for Connexxia, a small technology company in Atlanta. He now works as a Software Engineer for Google. (more…)

now reading



syndicated feeds


 physical language workshop attribution-sharealike license / / xhtml / css