⬆️ ⬇️

Why you should use XMLHttpRequest asynchronously

* Per .: The thought of this topic is trivial and should be known to everyone: use asynchronous requests. But statistics is a harsh thing, and, apparently, not everyone knows it. And the consequences, in fact, occur in all browsers. *



8.4% of all page hangs in IE9 over the past month are due to the fact that XMLHttpRequest objects block the UI stream with a synchronous request. This is a huge number! With a few code changes available, these freezes can be easily avoided - and developers can provide their users with a much better experience working with their sites. We will look at what happens when it hangs, what you can do about it, and we will also try to do a small demonstration to see firsthand what happens when a synchronous request hangs the browser.



Synchronous XMLHttpRequest blocks UI thread



We already know that starting blocking operations in the UI thread can lead to problems . We have already written about this before. This may not be obvious, but this is exactly what happens when the XMLHttpRequest.send () method is called synchronously (for example, by passing the bAsync attribute to false when calling the open () method). The UI thread must wait until it receives a response from the server or until a timeout occurs for the request. While waiting, messages do not pass.



Why bother to use synchronous XMLHttpRequest at all?



Developers tend to use synchronous requests instead of asynchronous requests simply because it is easier and can lead to less complex code. In this discussion on StackOverflow, there are some strong reasons for why you might want to use one instead of the other, but nothing substantial enough that would justify the risk.

')

How to fix…



There are at least two ways you can fix your code to avoid hangs. The first method is preferable, since you will not block the UI thread. The second method is less optimal, since you will still block, but for less time than you most likely do now.

  1. Write your code so that requests are asynchronous.



    You need to pass a true value for the bAsync parameter in the open () method , and you will also need to write a handler for the onreadystatechange event . On the Internet there are many examples of how this can be done, for example, this one .
  2. Set the timeout property.



    Since the Desktop Window Manager detects a hangup after 5 seconds without responding, and restoring after a hangup in IE triggers after 8, I recommend setting the timeout property to no more than 5 seconds. You may also want to put a handler to react to the ontimeout event .



    (How do I know these numbers? Read more here and here )




How about an example?



Let's see how this all works, in more detail, on a simple scenario:

  1. We will write code that sends a synchronous request and hangs the browser with it, and further
  2. let's replace the code with asynchronous and look at the changes in the behavior of the browser.


The purpose of this scenario is to show how the transition from a synchronous request to an asynchronous request improves the user experience of working with your application.



WARNING: This example will freeze your browser and may lead to unpredictable and even undesirable results on the client side as well as on the server side. In other words, do not run it on a production server or any other machine that cannot correctly work out unstable and potentially incorrect code.



Installation



You will need a server that can run ASP.NET pages if you want to repeat the example below. I understand that perhaps not all readers use ASP.NET, but the code should be transparent enough so that you can easily reproduce it in a different environment.



I also assume that you are familiar with the operation of the web server and know how to run pages with server logic.



Copy this code into your favorite text editor and save it as hangme.aspx somewhere on your server.



<!-- code starts after this line --> <%@ Page Language="C#" %> <html> <head> <title>XmlHttpRequest open hang test</title> <script runat="server"> protected void Page_Load(object sender, EventArgs e) { if(Request.QueryString["hang"] == "1") { int seconds = 0; Int32.TryParse(Request.QueryString["seconds"], out seconds); System.Threading.Thread.Sleep(seconds * 1000); } } </script> <script type="text/javascript"> function call_hangme() { var oReq; if (window.XMLHttpRequest) { oReq = new XMLHttpRequest(); } if (oReq != null) { var sUrl = "http://localhost/hangme.aspx?hang=1&seconds=360"; // change localhost to your server name if applicable document.getElementById("txt1").innerHTML = "Request Sent..."; // pass false for the bAsync parameter for a synchronous request oReq.open("GET", sUrl, false); oReq.send(); document.getElementById("txt1").innerHTML = "Response Received!"; } else { window.alert("Error creating XmlHttpRequest object."); } } </script> </head> <body> <p>Click this button to hang the browser</p> <form name="form1" action="" method="get"> <input type="button" name="btn1" value="hang me" onClick="call_hangme()"> </form> <p id="txt1"/> </body> </html> <!-- code ends on the line before this one --> 




Let's hang it up!



Now,

  1. Open Internet Explorer and go to the hangme.aspx page.
  2. Click on the “hang me” button.




Wow, wow! We are stuck! Since we told the stream to sleep for 6 minutes, we’ll have to quash a bit here. If you use IE9, you will surely notice a golden dice bottom offering a “Restore Page”. If you use a different browser, you will most likely get a frozen window.



We use asynchronous opening of request



Copy this code to a new file and save it as wont_hangme.aspx .



 <!-- code starts after this line --> <%@ Page Language="C#" %> <html> <head> <title>XmlHttpRequest open hang test</title> <script runat="server"> protected void Page_Load(object sender, EventArgs e) { if(Request.QueryString["hang"] == "1") { int seconds = 0; Int32.TryParse(Request.QueryString["seconds"], out seconds); System.Threading.Thread.Sleep(seconds * 1000); } } </script> <script type="text/javascript"> function call_hangme() { var oReq; if (window.XMLHttpRequest) { oReq = new XMLHttpRequest(); } if (oReq != null) { var sUrl = "http://localhost/wont_hangme.aspx?hang=1&seconds=360"; // change localhost to your server name if applicable document.getElementById("txt1").innerHTML = "Request Sent..."; // pass true for the bAsync parameter /* ===> */ oReq.open("GET", sUrl, true); /* Here we define an anonymous function for the onreadystatechange event. We check if we received all the data, and the request was successful before changing the text */ /* ===> */ oReq.onreadystatechange = function() { if (oReq.readyState == 4 && oReq.status == 200) { document.getElementById("txt1").innerHTML = "Response Received!"; } } oReq.send(); } else { window.alert("Error creating XmlHttpRequest object."); } } </script> </head> <body> <p>Click this button to hang the browser</p> <form name="form1" action="" method="get"> <input type="button" name="btn1" value="hang me" onClick="call_hangme()"> </form> <p id="txt1"/> </body> </html> <!-- code ends on the line before this one --> 




The new code is highlighted ( Per .: unfortunately, the code on the Habré did not work out, but we are talking about the line after the comment "// pass true for the bAsync parameter" and then installing the onreadystatechange handler ). Now we call the open () method with the bAsync = true parameter, which means that we are making an asynchronous call. This will free up the UI thread for other tasks instead of waiting for a response or timeout. If we are not blocked, we can send a message, and if so, then we are not stuck!



Now, when the response from the server comes back to us sooner or later, we need a function to process it. This is exactly what the highlighted code does. This is our handler for the onreadystatechange event . As you can see, I just check that the request was successful ( status == 200) and that all data was received ( readyState == 4).



Can we hang again?



Again,

  1. Open Internet Explorer and go to wont_hangme.aspx.
  2. Click the “hang me” button.




VoilĂ ! As you can see, the request has been sent, but the browser is not frozen. When the answer does come back to us, the text should change; but be that as it may, you can still interact with the page.



Finally



As stated at the beginning, you should make your XMLHttpRequest requests asynchronous. This example clearly shows why. You will increase the responsiveness of your pages, you can avoid hang-ups, and, most importantly, provide your users with a more pleasant interaction with your applications.

Source: https://habr.com/ru/post/126571/



All Articles