UI thread blocking problem and solution caused by jQuery synchronous Ajax

I encountered the UI thread blocking problem caused by synchronous Ajax, and recorded it here.

The reason is this, because there are multiple similar asynchronous request actions on the page, in line with the principle of improving code reusability, I encapsulated a function named getData, which receives different parameters, is only responsible for obtaining data, and then puts data return. The basic logic is stripped out like this:

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

    return result;
}

 

    The ajax here cannot be asynchronous, otherwise when the function returns, the result has not been assigned, and an error will occur. So I added async:false. It looks like there is no problem. I can get the data normally by calling this function.
$(".btn1").click(function() {
    var data = getData1();
    alert(data);
});

 

 

     Next, I need to add another function. Since the ajax request takes a certain amount of time, I need to have a loading effect on the page before sending the request, that is, a gif picture of "loading" is displayed, which must be seen by everyone. So my handler function becomes like this: 

 

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

 

    Show the loading image before the request and hide it after the request is complete. It doesn't seem to be a problem. In order to see the effect, my p.php code sleeps for 3 seconds, as follows:

 

<?php
sleep(3);
echo ("aaaaaa");
?>
    But when I run it, the problem occurs. When I click the button, the loading image does not appear as expected, and the page does not respond. After excluding for a long time, I found the reason, which is in async:false. The rendering (UI) thread of the browser and the js thread are mutually exclusive, and page rendering will be blocked when executing js time-consuming operations. There is no problem when we perform asynchronous ajax, but when set to synchronous request, other actions (code behind the ajax function, and the rendering thread) are stopped. Even though my DOM manipulation statement is before the request, this synchronous request "quickly" blocks the UI thread, giving it no time to execute. That's why the code fails.
    

setTimeout solves blocking problem

Now that we understand where the problem is, let's find a way to target it. In order to prevent the synchronous ajax request from blocking the thread, I thought of setTimeout, put the requested code in sestTimeout, and let the browser restart a thread to operate, won't it solve the problem? So, my code becomes 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 will execute it after a set minimum time. Regardless of the 3721, run it first and see.

As a result, the loading picture is displayed, but! ! ! Why doesn't the picture move, I'm obviously a dynamic gif. At this time, I quickly thought that although the synchronous request is delayed, the UI thread will still be blocked during its execution. This blockage is pretty awesome, even the gif image doesn't move, it looks like a static image.

The conclusion is obvious that setTimeout can cure the symptoms but not the root cause, which is equivalent to "slightly" asynchronizing the synchronous request, and then it will still enter the nightmare of synchronization and block the thread. Scenario failed.

 

Time to use Deferred

After jQuery version 1.5, the Deferred object was introduced, which provided a very convenient generalized asynchronous mechanism. For details, please refer to this article by Mr. Ruanyifeng http://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 removed async:false in the ajax request, which means that the request is asynchronous again. Also, please pay attention to this sentence in the success function: defer.resolve(data), the resolve method of the Deferred object can pass in one parameter, any type. This parameter can be obtained in the done method, so the data we request asynchronously can be returned in this way.

So far, the problem has been solved. Deferred objects are so powerful and convenient that we can put them to good use.

 

     Final note: This article is taken from other sites and is only used as a record (my own ability to organize is slightly weak :)) 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326696840&siteId=291194637