Blocking jQuery.ajax requests and jQuery.ajaxQueue
by Kristian Kraljic, April 17, 2016
With AJAX, web applications can send data to and retrieve from a server (as the name suggests) asynchronously. Well, that’s not entirely true. Most AJAX libraries and APIs do indeed provide functions to retrieve data synchronously… even if it sounds like a contradiction. In jQuery for example, you may set the
async parameter to
false, in order to do a synchronous request. Doing synchronous requests from time to time makes a lot of sense, even in modern web applications given the right use case. Now you might ask yourself, "well if I don’t do it often and process the data asynchronously otherwise, what could possibly go wrong?". In this article I would like show you one case, in which mixing synchronous and asynchronous AJAX had completely messed up the web experience of one of my applications.
I was developing on a small web application to monitor multiple backend systems. The application displayed a list of all the systems, including a status display. At the very start of the application and in regular intervals, the application would do a AJAX request to the backend system, checking the response and measuring the response time. So far, so easy. For each backend system I filed one asynchronous AJAX request. We speek about one- to two hundred servers, so one- to two hundred asynchronous AJAX requests at the start of the application and when the refresh interval hit.
Everything was working out fine, until I added a detail page to the application, displaying some additional information on a system. I decided to query the information with a synchronous web service call using AJAX, as soon as the user clicks on one of the lists entries. Doing so, I accidentally stubmbeled upon a problem, I wasn’t aware of in the first place. The behaviour of my web application changed, and as soon as I clicked on a system, the whole application froze for up to 20 seconds flat! But hey, that’s the problem of synchronous polling, you might say? Well, as it turned out, not in this case supprisingly.
My first simple guess was the same as yours: the detail web service is too slow, so the page starts to lag. I soon found out, that was not actually the case. Calling the detail web service using a REST client, returned data in under 20ms. But why the heck does the application freeze then? I did a network trace and what I found out surprised me at first, but became all clear soon after. The asynchronous requests are blocking the synchronous requests from executing. How was this possible? I mean, the requests are asynchronous. The answer is quite simple. Even modern browsers, have a limit on how many network connections they execute in parallel, so how many network threads are available. In most browsers this varies between two (mostly mobile) and six, sometimes up to eight. My applications did a lot of asynchronous requests at the start of the application. With all the network threads blocked, the browser automatically stalls all upcoming requests, trying to process them in order and as quickly as possible. So even if most the the requests have been made asynchronously, the synchronous request was added to the very same request queue of the browser. In my case this lead to a huge delay, as the user was waiting for the one synchronous request, which was added to the end of the request queue.
How to prevent such a behaviour? The answer is again quite simple: don’t do more than two to eight (depending on the browser), network requests in parallel. That’s sounds kind of impractical, when speaking of modern web applications, but in most cases it’s unlikely for the user to feel a tremendous decrease in performance, whether each of the list elements refreshes one after another, or in chunks of the number of network threads available.
jQuery.ajaxQueue function to my
jQuery object. The function behaves similar to jQuerys
ajax function, taking a queue name as third parameter, so you can have multiple "network queues" running in parallel:
The return value is not a jqXHR, but it will behave like one. I decided to put my
jQuery.ajaxQueue function on GitHub Gist, for everyone free to use.