javascript中的setTimeout函数太慢

问题描述

所以我用JavaScript和HTML为项目创建了一个秒表,它只是一个通用的秒表,对于start_timer()函数,我使用了setTimeout()函数来确定速度。 但是,当我在浏览器中运行文件时,由于秒数不同步,它们似乎计数太慢。 此处的视频说明了时钟有多慢:https://drive.google.com/file/d/1mqB4w5ZX7YqW8C57HhgWtIC63eyfKaGe/view?usp=sharing

我觉得这是一个非常愚蠢的问题,但是非常感谢;)我想知道是否需要使用网络工作者或其他工具(这是我第一个全面的javascript项目,因此我是新手。) 下面的代码

var isTiming = false; //as mentioned in the Design,this is to determine whether the stopwatch IS counting or IS NOT
var speed = 10;  //10 milliseconds count allowing for hundredths of a millisecond for better accuracy
var binaryTrue = 0; //checks to see if the stopwatch is paused
function start_stopwatch() { //function to start the counting
  if (isTiming == true) {
    var stopwatch = document.getElementById("TiMetable").innerHTML; //set the stopwatch to the digital clock in the HTML


    //use of an array to display elapsed time in the format hh:mm:ss:lll
    //each chronological place value is at a different spot in the array from left to right (numeric values)
    var arr = stopwatch.split(":");
    /*splits the array by separating each value (seconds,minutes,etc.) everytime there is a ":"
                                        when a button is pressed */
    var hr = arr[0]; //first position :hr: at the first space of the array
    var min = arr[1]; //seccond position :mm at the second space of the array
    var sec = arr[2]; //third position :ss at the third space of the array
    var millisec = arr[3]; //fourth position :lll at the fourth space of the array


    //Counting up in normal digital clock format,executes every 10 milliseconds which allows for hundredths of a second
    //returns next value after 10 milliseconds are up
    millisec++;
    if (millisec < 10) {
      millisec = "0" + millisec;
    }
    if (millisec / 100 === 1) {
      millisec = 0;
      millisec = "0" + millisec;
      if (sec / 59 === 1) {
        sec = 0;
        sec = "0" + sec; //added in after the place value displayed as "0" and not "00"
        if (min / 59 === 1) {
          hr++;
          min = 0;
          min = "0" + min; //added in after the place value displayed as "0" and not "00"
          sec = 0;
          sec = "0" + sec; //added in after the place value displayed as "0" and not "00"
        } else {
          min++;
          if (min < 10) {
            min = "0" + min;
          }
        }
      } else {
        sec++;
        if (sec < 10) {
          if (sec < 1) {
            sec = "0" + "0" + sec;
          } //added in after it was displayed as "0" not "00"//
          else {
            sec = "0" + sec;
          }
        } else {
          if (sec < 1) {
            sec = "0" + sec;
          }
        }
      }
    }


    //update the HTML digital time
    document.getElementById("TiMetable").innerHTML = hr + ":" + min + ":" + sec + ":" + millisec; //modify the array to the updated time
    setTimeout(start_stopwatch,(speed /* * ((6/7)/1.072)*/ )); //THIS TENDS TO BE OUT OF SYNC AS IT IS SLOWER THAN A norMAL CLOCK. SO * ((6/7)/1.072) TO IMPROVE SPEED.
    document.getElementById("control").innerHTML = "Pause";
  }
}


//function to allow the user to change the speed of counting as desired,and as many times as they like
//
function change_speed() {
  speedInput = window.prompt("Enter speed,where '1' = 1 second or '2' counts twice as fast,or '0.5' counts slower.");

  //Input validation/verification as the user should only input an actual number,whether a float or an integer.
  //isNaN is a useful function here where NaN means "Not a Number"
  if (isNaN(speedInput)) { //if the value that the user puts in to change the speed into isn't actually (only) a number
    speedInput = window.prompt("Enter speed AS A NUMBER where '1' = 1 second or '2' counts twice as fast,or '0.5' counts slower. Numbers only.");
  }

  speedLength = speedInput.length; //defensive design in the form of input validation - minimum length
  speedInputHolder = speedInput;
  speedInput = 1 / speedInputHolder; //converts speed requested by the USER into counting length (e.g. 2x speed = 0.5 second length,which is 2x faster,so accessible user interface)

  if (speedInput === null) { // "Cancel" button pressed
    speed = speed;
  } else {
    if (speedLength > 0) {
      speedInputInMilliseconds = (speedInput * 10); //not * 1000 as tenth-seconds are included too
      speed = (speedInputInMilliseconds /** ((60/7)/1.072)*/ ); //need to make sure the 4.275 method still syncs*/
    }
  }
  if (speedInputHolder == 1) {
    var indicateSpeed = document.getElementById("speedindicator").innerHTML = "Counting speed:  " + "(" + (1 / speedInput) + "x speed" + ")";
    var indicateInterval = document.getElementById("FractionalIndicator").innerHTML = "Counting frequency: " + speedInput + " second."
  } else {
    var indicateSpeed = document.getElementById("speedindicator").innerHTML = "Counting speed:  " + "(" + (1 / speedInput) + "x speed" + ")";
    var indicateInterval = document.getElementById("FractionalIndicator").innerHTML = "Counting frequency: " + speedInput + " seconds."
  }
}

function change_state() { //function to change from start to pause or pause to start
  if (isTiming == false) {
    isTiming = true;
    binaryTrue = 1;
    start_stopwatch();
    var started = document.getElementById("control").innerHTML = "Pause";
    console.log("Timer started.");
  } else {
    stop_stopwatch();
    console.log("Timer stopped.");
  }
}

function reset() {
  if (binaryTrue == 1) {
    var reset = document.getElementById("TiMetable").innerHTML = "00" + ":" + "00" + ":" + "00" + ":" + "00";
    if (isTiming == false) {
      var started = document.getElementById("control").innerHTML = "Start";
    }
    console.log("Timer reset.");
  } else {
    if (binaryTrue == 0) {
      var reset = document.getElementById("TiMetable").innerHTML = "00" + ":" + "00" + ":" + "00" + ":" + "00";
      if (isTiming == false) {
        var started = document.getElementById("control").innerHTML = "Start/Pause";
        console.log("Timer reset.");
      }
    }
  }
}

//Pausing the stop watch,so STOPPING it from continuing counting
//Set the isTiming variable as FALSE which controls the start_timer function from continuing
function stop_stopwatch() {
  isTiming = false;
  document.getElementById("control").innerHTML = "Resume";
  console.log("Timer stopped.");
}


//the same as stop_stopwatch but also RESETS the stopwatch

/* if isTiming is already FALSE and so the timer is paused
change button label into "Start/Pause"
otherwise make isTiming to be FALSE and reset the timer to 00:00:00:00
and reset the label to "start"
(leave the label as "Start/Pause" if the stopwatch isn't used at least once yet so that the user can still see the original instructions")*/
function stop_and_reset_stopwatch() {
  if (binaryTrue == 0) { //if the timer hasn't been used at least once
    if (isTiming == false) {
      document.getElementById("control").innerHTML = "Start/Pause"; //the user won't have kNowledge of the instructions yet
    }
  } else {
    isTiming = false;
    document.getElementById("TiMetable").innerHTML = "00" + ":" + "00" + ":" + "00" + ":" + "00";
    if (binaryTrue == 0) { //if the stopwatch HASN'T been used yet at least once,meaning that binaryTrue is still 0
      document.getElementById("control").innerHTML = "Start/Pause";
    } else { //if the stopwatch HAS been used at least once,meaning that binaryTrue is 1
      document.getElementById("control").innerHTML = "Start";
    }
  }
  console.log("Timer stopped and reset.");
}
<link href="https://fonts.googleapis.com/css2?family=Dosis:wght@600&display=swap" rel="stylesheet">
<div class="Stopwatch">
  <div class="Speedindicator">
    <div class="IntervalIndicator">
      <div class="CountTable">
        <p><span id="TiMetable" style="color: #00ffbf; font-size: 200px;">00:00:00:00</span></p>
        <div class="Controllers">
          <button id="control" onclick="change_state();">Start/Pause </button>
          <button id="reset" onclick="reset();">Reset </button>
          <button id="stop" onclick="stop_and_reset_stopwatch();">Stop and Reset</button>
          <button id="speed" onclick="change_speed();">Change speed</button>
          <script type="text/javascript" src="timer.js"></script>
          <p><br><span id="Label" style="color: #00ffbf; font-size: 100px; font-weight: bold; font-family: Arial,Helvetica,sans-serif">hr:min:sec:hundredth</span></br>
          </p>
          <heading id="speedindicator" style="color: #00aba3; font-size: 20px; font-family: Arial">Stopwatch speed: 1 second (default) </heading>
          <br>
          <heading id="FractionalIndicator" style="color: #00aba3; font-size: 10px; font-family: Arial"></heading>
          </br>

解决方法

setTimeout确保将在不超过指定时间的情况下调用一个函数。这是一种非常差的时间测量机制。

通常,如果您需要经常更新某些内容,则应改用requestAnimationFrame

无论您是否切换到它,都不要尝试数秒。

创建一个日期对象来存储您的开始时间,然后每次执行更新时:将当前时间与开始时间进行比较。

例如:

document.querySelector("button").addEventListener("click",() => {

  const start = moment();

  requestAnimationFrame(function update() {
    const now = moment();
    const duration = moment.duration(now.diff(start));
    document.querySelector('output').value = duration.asSeconds();
    requestAnimationFrame(update);
  });

});
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.0/moment.min.js"></script>

<output>
<button>Start</button>