【问题标题】:async XMLHttpRequest blocking UI in Browser浏览器中的异步 XMLHttpRequest 阻塞 UI
【发布时间】:2019-11-26 05:42:56
【问题描述】:

我在 Web 应用的客户端有以下功能:

function fetchDataFromApi(fetchCode, options, callback) {

    var dataObject = JSON;
    dataObject.fetchCode = fetchCode;
    dataObject.options = options;

    var xhr = new XMLHttpRequest();
    var url = "DATA_API_URL"; 

    // connect to the API
    xhr.open("POST", url, true);
    xhr.setRequestHeader("Content-Type",
        "application/json"
    ); 

    // set callback for when API responds. This will be called once the request is answered by the API.
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4 && xhr.status === 200) {
            // API has responded; 
            var json = {
                ok: false,
                message: 'could not parse response'
            };
            try {
                // parse the raw response into the API response object
                json = JSON.parse(xhr.responseText);
            } catch (err) {
                // probably json parse error; show raw response and error message
                console.log(err);
                console.log("raw response: " + xhr.responseText);
            } 

            if (json.ok) {
                // success, execute callback with argument json.data
                callback(json.data);
            } else {
                // fetch failed; 
                console.error(json.message);
            }
        }
    }; 

    // send request payload to API
    var data = JSON.stringify(dataObject);
    xhr.send(data);
}

由于我使用的是异步调用(xhr.open中的第三个参数设置为true),我惊讶地发现这个函数会阻塞浏览器中的UI。当使用此功能从服务器抓取大量数据时,可能需要 3-4 秒,阻塞 UI 并在 Chrome 控制台中生成此错误:

[Violation] 'load' handler took 3340ms

这个函数是currently in production here,我在这里调用这个函数:

function getNamesFromApi() {
    fetchDataFromApi('chj-confraternity-list', {}, function (data) {
        fadeReplace(document.getElementById('spinner-2'), document.getElementById(
                'name-list-container'),
            false, true);
        // transaction was successful; display names
        var listString = "";
        if (data.list) {
            // add the names to the page
            var listLength = data.list.length;
            for (var x = 0; x < listLength; x++) {
                document.getElementById('name-list-container').innerHTML +=
                    "<div class='name-list-item'>" +
                    "<span class='name-list-name'>" +
                    data.list[x].name +
                    "</span>" +
                    "<span class='name-list-location'>" +
                        data.list[x].location +
                    "</span>" +
                    "</div>";
            }
        }
    });
}

window.addEventListener('load', function() {
    getNamesFromApi();
});

为什么这会阻塞 UI,我在制作异步 XMLHttpRequest 时做错了什么?

更新:感谢 cmets 为我指明了正确的方向;问题不是 XMLHttpRequest,而是我在循环中附加了 innerHTMl。问题现已修复,答案中包含更正后的 sn-p。

【问题讨论】:

  • 什么时候调用函数?
  • @Itamar 我通常在页面加载时调用该函数,有时在一些用户操作之后(比如更新一些信息)
  • 听起来很奇怪。服务器响应需要那么长时间吗?你怎么知道用户界面实际上被阻止了?
  • 您应该运行您的代码以在 Web Worker 中生成 HTML。这就是阻止 UI 而不是 XHR 的原因。另外,不要在循环中附加到 innerHTML,这是一个非常昂贵的操作。你的网络工作者应该返回一个你只分配一次的字符串给innerHTML
  • 无需更新,您自己的回答已经表明更新了。即使您使用了 cmets 的提示,也可以回答您自己的问题。

标签: javascript asynchronous browser xmlhttprequest


【解决方案1】:

UI 被阻止是因为我在一个循环中附加了 innerHTML,这是一个昂贵的 UI 阻塞操作。该问题现已修复。这是更正后的 sn-p:

function getNamesFromApi() {
    fetchDataFromApi('chj-confraternity-list', {}, function (data) {
        fadeReplace(document.getElementById('spinner-2'), document.getElementById(
                'name-list-container'),
            false, true);
        // transaction was successful; display names
        if (data.list) {
            var listString = "";
            // add the names to the page
            var listLength = data.list.length;
            for (var x = 0; x < listLength; x++) {
                listString +=
                    "<div class='name-list-item'>" +
                    "<span class='name-list-name'>" +
                    data.list[x].name +
                    "</span>" +
                    "<span class='name-list-location'>" +
                        data.list[x].location +
                    "</span>" +
                    "</div>";
            }
            document.getElementById('name-list-container').innerHTML = listString;
        }
    });
}

window.addEventListener('load', function() {
    getNamesFromApi();
});

【讨论】:

  • @JuanMendes 也许在未来,我将不得不调查这些。谢谢
猜你喜欢
  • 2016-02-15
  • 2017-08-04
  • 1970-01-01
  • 2016-03-13
  • 1970-01-01
  • 1970-01-01
  • 2015-05-14
  • 2017-07-07
  • 2021-05-26
相关资源
最近更新 更多