当tab不活跃时候怎样搞一个定时器

ps: 下面的脚本使用了 script module 请打开原文或者使用非 IE 浏览器浏览

setTimeout 当 tab 不活跃时候

当 tab 不活跃的时候,setTimeout 和 setInterval 的运行时间出现延迟

当未被激活的 tabs 的 setInterval 的最小延迟>=1000ms.浏览器这样做的原因是为了省电.

点击下面按钮,网页的 document title 会数字相加,闪烁间隔为 500ms,当 tab 切换为其他 tab 的时候就会变慢

<script type="module">
  let button = document.getElementById("setTimeout");
  let id;
  let count = 0;
  let flag = true;
  button.onclick = () => {
    if (flag) {
      button.innerText = "关闭定时器";
      id = setInterval(() => {
        document.title = `${count++} setInterval`;
      }, 500);
      flag = !flag;
    } else {
      button.innerText = "开启定时器";
      clearInterval(id);
      flag = !flag;
    }
  };
</script>

requrestAnimationFrameAPI 当 tab 不活跃时候

requrestAnimationFrameAPI 当 tab 不活跃时候,执行会被暂停
注意 requrestAnimationFrameAPI 只是能够满足 1 秒 60 帧,所以会和比 setTimeout 慢一些

<script type="module">
  const timerList = {};

  function setRafTimeout(cb, time) {
    const timer = {
      id: -1,
      start: -1,
      cb,
    };
    const handleNextTick = (timestamp) => {
      if (timer.start === -1) {
        timer.start = timestamp;
      }
      const elapsed = timestamp - timer.start;
      if (!time || elapsed > time) {
        cb();
        clearRafTimeout(id);
      } else {
        timer.id = window.requestAnimationFrame(handleNextTick);
      }
    };
    const id = window.requestAnimationFrame(handleNextTick);
    timer.id = id;
    timerList[id] = timer;
    return id;
  }

  function clearRafTimeout(id) {
    const timer = timerList[id];
    if (timer) {
      window.cancelAnimationFrame(timer.id);
      delete timerList[id];
    }
  }
  let intervalList = {};
  function setRafInterval(cb, time) {
    let intervalId = setRafTimeout(function fn() {
      if (!intervalList[intervalId]) return;
      cb();
      clearRafTimeout(intervalList[intervalId]);
      intervalList[intervalId] = setRafTimeout(fn, time);
    }, time);
    intervalList[intervalId] = intervalId;
    return intervalId;
  }
  function clearRafInterval(id) {
    const timer = intervalList[id];
    if (timer) {
      window.cancelAnimationFrame(timer);
      delete intervalList[id];
      console.log(timer, id, intervalList);
    }
  }

  let requestButton = document.getElementById("requestAnimation");
  let requestId;
  let count = 0;
  let flag1 = true;
  requestButton.onclick = () => {
    if (flag1) {
      requestButton.innerText = "关闭requestAnimation定时器";
      requestId = setRafInterval(() => {
        document.title = `${count++} requestAnimation`;
      }, 1000);
      flag1 = !flag1;
    } else {
      requestButton.innerText = "开启requestAnimation定时器";
      clearRafInterval(requestId);
      flag1 = !flag1;
    }
  };
</script>

WebWorker

webworker 不会受浏览器切换 tab 影响,能够正常运行

<script type="module">
  let blob = new Blob([
    `
    let timer;
    onmessage = ({data:flag})=>{
        console.log("收到message",flag)
        if(!flag){
            clearInterval(timer);
        }else{
           timer = setInterval(function() { postMessage(''); }, 500);
        }
    }
    `,
  ]);
  let url = window.URL.createObjectURL(blob);
  var worker = new Worker(url);
  window.URL.revokeObjectURL(blob);
  let workerButton = document.getElementById("webwork");
  let requestId;
  let count = 0;
  let flag1 = true;
  worker.onmessage = () => {
    console.log(count, "123");

    document.title = `${count++} WebWorker`;
  };
  workerButton.onclick = () => {
    console.log(flag1);
    if (flag1) {
      workerButton.innerText = "关闭web work定时器";
    } else {
      workerButton.innerText = "开启web work定时器";
    }
    worker.postMessage(flag1);
    flag1 = !flag1;
  };
</script>