首页 > 解决方案 > setInterval() 没有跟上速度

问题描述

我正在我正在开发的网站上制作画廊。它会自动滚动浏览照片,但用户也可以暂停和手动滚动。我遇到的问题是自动滚动时间。我做了一个每毫秒setInterval()运行一次。autoScroll()在 中autoScroll(),它增加一个变量,然后滚动并在变量达到 3000 时将变量设置回 0。我正在这样做,而不是在直接调用滚动的 setInterval 上使用 3000 毫秒,以便我可以重置计时器当用户手动暂停或滚动时。理论上,它应该每 3 秒滚动一次。但它比这慢得多。在 Chrome 中大约需要 12 秒,在 Firefox 中大约需要 45 秒。我还没有在任何其他浏览器上测试过它。

目前,我直接在我的电脑上运行它,所以还没有涉及服务器。所以这应该是代码问题而不是服务器问题。

这是脚本引用的相关 HTML:

<table>
  <tr>
    <th>
      <button id="left">&#10094;</button>
    </th>
    <th id="picture" colspan="3"></th>
    <th>
      <button id="right">&#10095;</button>
    </th>
  </tr>
  <tr>
    <td></td>
    <td id="pause">&#10074;&#10074;</td>
    <td id="below"><span id="caption" style="text-align: center;"></span><br><span id="dots"
        style="text-align: center;"></span></td>
    <td style="width: 5%"></td>
    <td></td>
  </tr>
</table>

这是javascript:

var pics = ["pic1.jpg", "pic2.jpg", "pic3.jpg", "pic4.jpg", "pic5.jpg"];
var captions = ["caption 1", "caption 2", "caption 3", "caption 4", "caption 5",];
var play = true;
var position = 0;
var direction = 0;
var auto = 0;

for (var x = 1; x < pics.length + 1; x++) {
  dots.innerHTML = dots.innerHTML + '<input id="dot' + x.toString() + '" type="radio" name="dot" value="' + x.toString() + '"><label for="dot' + x.toString() + '">&#9679;</label>';
}

dot1.checked = true;
picture.innerHTML = '<img src="' + pics[0] + '" />';
caption.innerHTML = captions[0];

function move() {
  var positionIndex = position + 1;
  position = position + direction;
  if (position == -1) { position = pics.length - 1; }
  if (position == pics.length) { position = 0; }
  positionIndex = position + 1;
  picture.innerHTML = '<img src="' + pics[position] + '" />';
  caption.innerHTML = captions[position];
  document.getElementById("dot" + positionIndex.toString()).checked = true;
  auto = 0;
}

left.onclick = function () {
  direction = -1;
  move();
}

right.onclick = function () {
  direction = 1;
  move();
}

dots.onclick = function () {
  position = parseInt(document.querySelector('input[name = "dot"]:checked').value) - 1;
  positionIndex = position + 1;
  picture.innerHTML = '<img src="' + pics[position] + '" />';
  caption.innerHTML = captions[position];
  document.getElementById("dot" + positionIndex.toString()).checked = true;
  auto = 0;
}

pause.onclick = function () {
  if (play) {
    play = false;
    pause.innerHTML = "&#9654;";
  } else {
    play = true;
    pause.innerHTML = "&#10074;&#10074;";
  }
  auto = 0;
}

function autoScroll() {
  auto++
  if (auto == 3000 && play) {
    direction = 1;
    auto = 0;
    move();
  }
}

window.setInterval(autoScroll, 1);

标签: javascriptsetinterval

解决方案


由于崩溃/幽灵错误缓解,所有浏览器都故意使计时器不那么精确。我不确定 setInterval 是否被认为是高分辨率计时器,但它也可能会受到影响。

https://developers.google.com/web/updates/2018/02/meltdown-spectre

甚至在此之前, setInterval 从未保证它会在提供的时间后精确触发。如果浏览器很忙,情况可能会变得更糟。

简而言之,您应该检查当前时间(例如使用 +(new Date())),而不是每个 setInterval 将变量增加 1 并查看实际经过了多少时间。

  ...
  auto = +(new Date());
}
function autoScroll() {
  var date = +(new Date());
  if(auto > date + 3000 && play) {
    direction = 1;
    auto = date;
    move();
  }
}

或者,正如已经建议的那样,放弃使用更短的 setInterval 的想法,保存对计时器的引用,然后取消该计时器并在用户单击时重新启动它。

  ...
  resetTimer();
}
function autoScroll() {
  if(play) {
    direction = 1;
    resetTimer();
    move();
  }
}
function resetTimer() {
   if(timer) {
       window.clearInterval(timer);
   }
   timer = window.setInterval(autoScroll,3000);
}
timer = window.setInterval(autoScroll,3000);

推荐阅读