Jquery ajax thread blocking problem caused by Jquery ajax synchronous blocking

  • 2020-11-18 05:14:16
  • OfStack

Recently, I was working on a project, and I encountered a problem with ui thread blocking caused by ajax synchronization. Now I will share my problem solving process with you.

It started because there were several similar asynchronous request actions on the page. In order to improve code reusability, I encapsulated a function called getData that took different parameters, only took the data, and then put the data into return. The basic logic is stripped down to this:


function getData1(){
    var result;
    $.ajax({
      url : "p.php",
      async : false,
      success: function(data){
        result = data;
      }
    });

  return result;
} 

ajax here cannot be used asynchronously, otherwise when the function returns, result has not been assigned yet and an error will occur. So I added async:false. It doesn't seem to be a problem. I call this function to get the data normally.


$(".btn1").click(function(){
    var data = getData1();
    alert(data);
});

Next, add another feature. Since ajax requests take a certain amount of time, I need to have an loading effect on the page before I send the request, showing a "loading" gif image, which you've all seen. So my handler looks like this:


$(".btn1").click(function(){
    $(".loadingicon").show();
    var data = getData1();
    $(".loadingicon").hide();
    alert(data);
});

Display the loading image before the request and hide it when the request is complete. It doesn't seem to be a problem. To see the effect, My p.php code sleep took 3 seconds, as follows:


<?php
sleep(3);
echo ("aaaaaa");
?>

But there was a problem when I ran it. I clicked the button and it didn't show up as expected, and the page didn't respond. It took a long time to find out the reason, which is async:false.

The browser render (UI) thread and js thread are mutually exclusive, and page rendering is blocked during time-consuming js operations. We have no problem executing asynchronous ajax, but when set to synchronous request, the rest of the action (the code after the ajax function, and the render thread) stops. Even if my DOM action statement is the first sentence of the request, this synchronous request "quickly" blocks the UI thread, giving it no time to execute. This is where the code fails.

setTimeout solves the blocking problem

Now that we know what the problem is, let's try to target it. To prevent synchronizing ajax requests from blocking the thread, I thought of setTimeout. Putting the requested code in sestTimeout and having the browser restart one thread to operate would solve the problem, wouldn't it? As a result, my code looks like this:


$(".btn2").click(function(){
    $(".loadingicon").show();
    setTimeout(function(){
      $.ajax({
        url : "p.php",
        async : false,
        success: function(data){
          $(".loadingicon").hide();
          alert(data);
        }
      });
    }, 0);
}); 

The second parameter of setTimeout is set to 0, and the browser executes after 1 set minimum time. Let's not worry about 372101 but let's run it and see.

The loading image shows up, but!! Why does the picture not move? I am clearly a dynamic gif picture. It quickly occurred to me at this point that although the synchronization request is delayed, it will block the UI thread during its execution. This block is so awesome that even the gif image doesn't move and looks like a still image.

The obvious conclusion is that setTimeout treats the symptoms rather than the root causes of the problem by making the synchronization request "slightly" asynchronous for a second, and then getting stuck in a synchronization nightmare that blocks the thread. The scheme failed.

It's time to go with Deferred

jQuery introduced Deferred objects after version 1.5, providing a convenient generalized asynchronous mechanism. Details can see this article http nguyen 1 peak teacher: / / www ruanyifeng. com blog / 2011/08 / a_detailed_explanation_of_jquery_deferred_object html.

So I rewrote the code with the Deferred object, as follows:


function getData3(){
    var defer = $.Deferred();
    $.ajax({
      url : "p.php",
      //async : false,
      success: function(data){
        defer.resolve(data)
      }
    });
    return defer.promise();
}  
$(".btn3").click(function(){
    $(".loadingicon").show();
    $.when(getData3()).done(function(data){
      $(".loadingicon").hide();
      alert(data);
    });
});

You can see that I dropped async:false from the ajax request, that is, the request is asynchronous again. defer. resolve(data), the resolve method of the Deferred object can pass in one argument, of any type. This parameter can be obtained in the done method, so the data from our asynchronous request can be returned in this manner.

So far, the problem has been solved. The Deferred object is so powerful and convenient that we can take advantage of it.

All of my test codes are as follows. Students who are interested can take them for testing 1.


<button class="btn1">async:false</button>
<button class="btn2">setTimeout</button>
<button class="btn3">deferred</button>
<img class="loadingicon" style="position:fixed;left:50%;top:50%;margin-left:-16px;margin-top:-16px;display:none;" src=http://www.update8.com/Web/Jquery/"loading2.gif" alt=" Being loaded " />
<script>
  function getData1(){
    var result;
    $.ajax({
      url : "p.php",
      async : false,
      success: function(data){
        result = data;
      }
    });
    return result;
  }
  $(".btn1").click(function(){
    $(".loadingicon").show();
    var data = getData1();
    $(".loadingicon").hide();
    alert(data);
  });
  $(".btn2").click(function(){
    $(".loadingicon").show();
    setTimeout(function(){
      $.ajax({
        url : "p.php",
        async : false,
        success: function(data){
          $(".loadingicon").hide();
          alert(data);
        }
      });
    }, 0);
  });
  function getData3(){
    var defer = $.Deferred();
    $.ajax({
      url : "p.php",
      //async : false,
      success: function(data){
        defer.resolve(data)
      }
    });
    return defer.promise();
  }  
  $(".btn3").click(function(){
    $(".loadingicon").show();
    $.when(getData3()).done(function(data){
      $(".loadingicon").hide();
      alert(data);
    });
  });</script>

ps:$.ajax parameter description

Parameters to describe

url required. Specify to which URL the request is to be sent.
data optional. Mapping or string values. Specifies the data that is sent to the server along with the request.
success(data, textStatus, jqXHR) is optional. The callback function that executes when the request succeeds.
dataType
Optional. Specifies the data type of the expected server response.
Intelligent judgment (xml, json, script, or html) is performed by default.


Related articles: