我经常创作定时器/秒表,而且我总是遇到问题。
使用setInterval();
时,很少准确。我正在使用Safari 8,在 20秒之后,它可以关闭 + - 8秒。我试图计算每个 10ms ,所以requestFrameAnimation
不会削减它,因为我没有动画。
我提出了以下使用Date
对象
var runs = 0,
speed = 10,
timeout = speed,
time = Math.floor(new Date().getTime() / speed);
function timer () {
runs += 1;
console.log(runs, new Date().getTime() / speed);
if (Math.floor(new Date().getTime() / speed) > time + 1) {
timeout = speed - (Math.floor(new Date().getTime() / speed) - time);
} else if (Math.floor(new Date().getTime() / speed) < time + 1) {
timeout = speed + (time - Math.floor(new Date().getTime() / speed));
} else {
timeout = speed;
}
time = Math.floor(new Date().getTime() / speed);
setTimeout(function () {
timer(); //Repeat
}, timeout);
}
timer();//Starts timer
除了在100毫秒内仍然可以进行3次(.6
,.64
,.68
),这大约是速度的三分之一。
我已经看到很多关于如何使用Node.js, Java, C#
等其他语言实现这一点的解决方案,甚至还有JavaScript库,但我似乎无法解决这个基本问题。
我有什么东西显然不见了吗?最好的方法是什么?
答案 0 :(得分:2)
如果您每10秒钟尝试运行一次,那么您的问题是您的初始速度设置为10吗? 1000 ms = 1秒,因此10/1000 = 1/100秒。此外,getTime函数返回Unix时间戳。除非您记录上一个时间戳,减去当前时间戳并调整执行时间内任何偏移的速度,否则除以速度并没有多大意义。
答案 1 :(得分:2)
有两种方法可以得到你想要的东西。
如果你可以支持web workers,那么你可以使用它们来运行一个专用的后台进程,它支持你想要做的时间帧推送类型(在一个时间间隔内逐帧增加一个计时器) ,没有时间戳差异,并保持准确)。
以下是此网页上的示例:
http://frenticb.com/tricks/simple-timer.php
<div class="header">A simple timer:</div>
<div class="timer" id="timer">00:00</div>
<div class="buttons">
<button onclick="startTimer()" id="button1">Start</button>
<button onclick="stopTimer()" id = "button2">Stop</button>
</div>
<script>
var w = null; // initialize variable
// function to start the timer
function startTimer(){
// First check whether Web Workers are supported
if (typeof(Worker)!=="undefined"){
// Check whether Web Worker has been created. If not, create a new Web Worker based on the Javascript file simple-timer.js
if (w==null){
w = new Worker("simple-timer.js");
}
// Update timer div with output from Web Worker
w.onmessage = function (event) {
document.getElementById("timer").innerHTML = event.data;
};
} else {
// Web workers are not supported by your browser
document.getElementById("timer").innerHTML = "Sorry, your browser does not support Web Workers ...";
}
}
// function to stop the timer
function stopTimer(){
w.terminate();
timerStart = true;
w = null;
}
</script>
simple.timer.js (请注意,网络工作者要求这是一个网址):
var timerStart = true;
function myTimer(d0){
// get current time
var d=(new Date()).valueOf();
// calculate time difference between now and initial time
var diff = d-d0;
// calculate number of minutes
var minutes = Math.floor(diff/1000/60);
// calculate number of seconds
var seconds = Math.floor(diff/1000)-minutes*60;
var myVar = null;
// if number of minutes less than 10, add a leading "0"
minutes = minutes.toString();
if (minutes.length == 1){
minutes = "0"+minutes;
}
// if number of seconds less than 10, add a leading "0"
seconds = seconds.toString();
if (seconds.length == 1){
seconds = "0"+seconds;
}
// return output to Web Worker
postMessage(minutes+":"+seconds);
}
if (timerStart){
// get current time
var d0=(new Date()).valueOf();
// repeat myTimer(d0) every 100 ms
myVar=setInterval(function(){myTimer(d0)},100);
// timer should not start anymore since it has been started
timerStart = false;
}
非网络工作者版本:
<p id='timer'>0.00</p>
<p id='starter-container'>
<button type='button' id='starter'>Start</button>
<button type='button' id='starter-reset'>Reset</button>
</p>
<script>
(function oab(){ // Keep it local.
var runs = 0,
max_runs = 10000,
speed = 10,
timeout = speed,
start_time = 0,
time = 0,
num_seconds = (30) * 1000,
mark_every = 100,
mark_next = time * speed,
timer_el = document.getElementById('timer'),
starter = document.getElementById('starter'),
reset = document.getElementById('starter-reset');
starter.addEventListener('click', function cl(){
reset_timer();
init_timer();
do_timer();
this.disabled = true;
});
reset.addEventListener('click', function cl(){
runs = max_runs++;
});
function init_timer() {
start_time = new Date().getTime();
time = Math.floor(start_time / speed);
}
function reset_timer() {
runs = 0;
starter.disabled = false;
timer_el.innerText = '0.00';
}
function do_timer(){
init_timer();
(function timer () {
var c_time = new Date().getTime(),
time_diff = c_time - start_time,
c_secs = 0;
runs += 1;
c_secs = (Math.round(time_diff / 10, 3) / 100).toString();
if (c_secs.indexOf('.') === -1) {
c_secs += '.00';
} else if (c_secs.split('.').pop().toString().length === 1 ) {
c_secs += '0';
}
timer_el.innerText = c_secs;
if (c_time >= mark_next) {
console.log(
'mark_next: ' + mark_next,
'mark time: ' + c_time,
'(' + (Math.floor(c_time * .01) * 100).toString().substring(10) + ')',
'precision: ' + (mark_next - c_time) + ')'
);
mark_next = Math.floor((c_time + mark_every) * .01) * 100;
}
if (Math.floor(c_time / speed) > time + 1) {
timeout = speed - ((c_time / speed) - time);
} else if (Math.floor(c_time / speed) < time + 1) {
timeout = speed + (time - Math.floor(c_time / speed));
} else {
timeout = speed;
}
time = Math.floor(new Date().getTime() / speed);
if (runs >= max_runs || time_diff > num_seconds) {
reset_timer();
return;
}
setTimeout(timer, timeout);
})();
}
})();
</script>
答案 2 :(得分:0)
您想要做的事情在技术上是不可能的,原因有两个:
运行代码的机器的速度
JavaScript的同步特性
要解释一下,如果计算机无法在10毫秒或100毫秒内执行指令,则不会执行。你可以做的最好是尝试调整延迟,但即使这样,机器正在执行的任何其他处理以及浏览器正在执行的任何其他JavaScript将阻止执行“线程”(我使用的术语松散) )。
我刚刚写了一些代码来测试它,因为我很好奇,平均而言,我能写的最有效的功能是在超过10秒的超时精确时间的67%之内。
我知道这是个坏消息,但这是事实。你想做的事情是无法实现的。