首页 > 解决方案 > Ajax 太慢 - 递归

问题描述

我用来请求页面的 ajax 代码消耗了太多内存,使浏览器变慢并且一切都滞后。似乎有递归正在进行,我不知道有什么方法可以防止它。这是代码的样子。

$(".item").each(function() {
    $this = $(this);
    var dataString = {s: "<?echo $_SESSION['currentview_'.$stamp]?>", r:"<?echo $search_usernumber?>", st: "<?echo $stamp?>"};
    $.ajaxSetup({cache:false});
    function timeLeft() {
        $.ajax({
            type: "POST",
            url: "get_content_home.php",
            dataType: "html",
            data: dataString, 
            success: function(result) {
                $this.html(result);
                //console.log("a");
                window.setInterval(function() {
                    timeLeft();
                }, 500);
            }
        });
    }
    timeLeft();
});

我怎么解决这个问题?提前致谢。

标签: javascriptajaxperformancerecursion

解决方案


递归的,你不应该使用这种形式的 nested setInterval。这样做会导致间隔实例的爆炸式增长。不要使用setInterval,而是使用 来安排其他请求setTimeout

setInterval将触发并在每个间隔继续触发,直到您告诉它停止。

setTimeout会开火一次。


让我们考虑以下代码,它应该解决您在这个问题中遇到的一些问题以及您的其他 2 个问题。

setInterval首先,正如我们之前所说,除非您真的希望它永远运行,否则不要使用。此外,除非您真的打算这样做,否则不要嵌套创作。setInterval

相反,让我们创建一个递归函数getTimeLeft()来处理触发请求并安排下一次检查一段时间后的剩余时间。

此示例还模拟了该$.ajax()函数,以便您可以看到该函数正在运行,因为我们没有要使用的实际后端。

// Fake server side data to keep track of time lefts
const timeLefts = {
  foo: 0,
  bar: 0,
  fizz: 0,
  buzz: 0
};
const timeLeftsUpdateInterval = setInterval(() => {
  for (const [key, val] of Object.entries(timeLefts)) {
    timeLefts[key] = Math.min(val + Math.random() * 10, 100);
  }
  if (Object.entries(timeLefts).every(([k, v]) => v >= 100)) {
    clearInterval(timeLeftsUpdateInterval);
  }
}, 1000);

// Mock $.ajax function to stub sending AJAX requests
function $ajax(kwargs) {
  return {
    done: cb => {
      setTimeout(() => {
        cb(timeLefts[kwargs.data.x]);
      }, 500);
    }
  };
}

// We will check for an update every second after the last request finishes
const timeLeftCheckInterval = 1000;

// Continuously check to see how much time is left for an element
function getTimeLeft(el) {
  // Make our request data
  const dataString = {
    s: "<?echo $_SESSION['currentview_'.$stamp]?>",
    r: "<?echo $search_usernumber?>",
    st: "<?echo $stamp?>",
    // My custom property to make this work
    x: el.dataset.item
  };

  // Make our request to get the time left
  const req = $ajax({ // Using our mock $.ajax
    type: "POST",
    url: "get_content_home.php",
    dataType: "html",
    data: dataString
  });

  // Once the request has finished
  req.done(data => {
    // Set the time left to the element
    el.innerHTML = data;

    // Have some condition so that you don't check for time left forever
    // Eventually there will be no time left right?  Why keep checking?
    if (data.timeleft <= 0) return;

    // Schedule another round of checking for time left after some duration
    setTimeout(() => {
      getTimeLeft(el);
    }, timeLeftCheckInterval);
  });
}

// Kick off getting timeleft for all .items
Array.from(document.querySelectorAll(".item"))
  .forEach(el => getTimeLeft(el));
<ul>
  <li class="item" data-item="foo"></li>
  <li class="item" data-item="bar"></li>
  <li class="item" data-item="fizz"></li>
  <li class="item" data-item="buzz"></li>
</ul>

此代码将解决您在2 Ajax 非阻塞中遇到的问题,因为每个元素都有自己的逻辑,即去往剩余时间并更新自身。

这也解决了您在 Ajax 中的 Timer - Preemption中可能面临的问题,因为现在元素不会再次检查以查看还剩多少时间,直到上一次检查完成之后。


推荐阅读