倒计时的几种方法

328 阅读3分钟

最近项目测试系统用到了倒计时,以下是总结的三种方法setTimeout()、requestAnimationFrame()、Web Worker()的例子

一 、setTimeout()

以下是使用 JavaScript 的 setTimeout() 方法实现一个简单的倒计时方法的示例:

function countDown(timeInSeconds) {
  // 将时间转换为毫秒
  const endTime = Date.now() + timeInSeconds * 1000;

  // 定义更新倒计时的函数
  function updateCountdown() {
    const timeLeft = endTime - Date.now();
    if (timeLeft <= 0) {
      // 倒计时结束,执行相应的操作
      console.log('倒计时结束');
      return;
    }

    // 将剩余时间转换
    const secondsLeft = Math.floor((timeLeft / 1000) % 60);
    const minutesLeft = Math.floor((timeLeft / 1000 / 60) % 60);

    // 在页面上更新倒计时显示
    console.log(`倒计时剩余${minutesLeft} 分钟 ${secondsLeft} 秒`);

    // 继续定时更新倒计时
    setTimeout(updateCountdown, 1000);
  }

  // 第一次更新倒计时
  updateCountdown();
}

// 调用 countDown() 方法开始倒计时
countDown(60); // 倒计时60秒

需要注意的是,在使用 setTimeout() 方法实现倒计时时,存在一定的误差,因为 setTimeout() 方法的精度不高,可能会出现延迟。如果需要更高精度的倒计时实现,可以使用 requestAnimationFrame() 方法或者使用 Web Workers 在后台执行任务。

二、requestAnimationFrame()

以下是使用 requestAnimationFrame() 方法实现一个定时器的示例,包括剩余分钟数和秒数的显示:

function timer(duration) {
  const startTime = Date.now();
  const endTime = startTime + duration * 1000;

  function updateTimer() {
    const currentTime = Date.now();
    const timeLeft = endTime - currentTime;

    if (timeLeft <= 0) {
      console.log('计时结束');
      return;
    }

    const secondsLeft = Math.floor((timeLeft / 1000) % 60);
    const minutesLeft = Math.floor((timeLeft / 1000 / 60) % 60);

    // 将剩余分钟数和秒数显示在页面上
    console.log(`剩余时间:${minutesLeft} 分钟 ${secondsLeft} 秒`);

    requestAnimationFrame(updateTimer);
  }

  requestAnimationFrame(updateTimer);
}

// 调用 timer() 方法开始计时,传入持续时间(以秒为单位)
timer(60);

requestAnimationFrame() 方法的精度相对较高,但是它的执行间隔并不稳定,因此在实现计时器时还需要考虑误差的问题。另外,由于 requestAnimationFrame() 方法是基于浏览器刷新率的,因此在不同的设备和浏览器上,其精度和性能可能会有所不同。

三、 Web Worker

使用 Web Worker 实现一个定时器的示例,包括剩余分钟数和秒数的显示:

timer.js 文件:

let timeLeft = 0;

function countdown(duration) {
  const endTime = Date.now() + duration * 1000;

  function updateTimer() {
    const currentTime = Date.now();
    timeLeft = endTime - currentTime;

    if (timeLeft <= 0) {
      postMessage({ done: true });
    } else {
      const secondsLeft = Math.floor((timeLeft / 1000) % 60);
      const minutesLeft = Math.floor((timeLeft / 1000 / 60) % 60);

      postMessage({ timeLeft: { minutes: minutesLeft, seconds: secondsLeft } });
      setTimeout(updateTimer, 1000);
    }
  }

  updateTimer();
}

onmessage = function (event) {
  countdown(event.data.duration);
};

在主线程中,我们可以使用 Web Worker 来创建一个新的计时器线程,发送消息并接收消息来更新计时器。以下是主线程代码:

let worker = new Worker('timer.js');

worker.onmessage = function (event) {
  if (event.data.done) {
    console.log('计时结束');
    worker.terminate();
  } else {
    console.log(`剩余时间:${event.data.timeLeft.minutes} 分钟 ${event.data.timeLeft.seconds} 秒`);
  }
};

// 调用 worker.postMessage() 方法开始计时,传入持续时间(以秒为单位)
worker.postMessage({ duration: 60 });

在这个示例中,我们创建了一个名为 timer.js 的文件,其中定义了一个名为 countdown() 的函数。在 countdown() 中,我们计算出计时结束时间 endTime,然后使用 updateTimer() 函数实现计时器的更新。如果剩余时间小于等于 0,表示计时结束,我们使用 postMessage() 方法向主线程发送一个带有 done 属性的消息。否则,我们计算出剩余的分钟数和秒数,并使用 postMessage() 方法向主线程发送一个带有 timeLeft 属性的消息。同时,我们使用 setTimeout() 方法来实现每秒更新一次计时器。

在主线程中,我们使用 new Worker() 方法创建一个新的 Web Worker,并定义一个 onmessage() 事件处理函数来处理从计时器线程发送的消息。如果接收到了 done 属性,表示计时结束,我们输出一条提示信息并终止计时器线程。否则,我们将接收到的剩余分钟数和秒数显示在页面上。

需要注意的是,Web Worker 可以在后台执行代码,不会阻塞主线程,因此它比定时器和 requestAnimationFrame() 更加可靠和稳定。但是,由于 Web Worker 中的代码是在独立的线程中执行的,因此它不能访问主线程中的 DOM 元素和一些浏览器 API,需要通过消息传递来与主线程进行通信。