JS:具有动态间隔的计时器功能得到 "off" 一秒(不同步)?
JS: Timer function with dynamic interval getting "off" by one second(out of sync)?
我有一个带有动态间隔的简单渐进式定时器功能。
目标:
让这个函数在尽可能少的执行中每 XX:00:00 或 XX:30:00 运行。它从每秒 运行ning 开始,然后每 5 秒、10 秒、30 秒等,直到每 30 分钟达到用户的 :30 分钟标记或新的小时标记。
(我最终将在 AngularJS 驱动的日程应用程序中使用它来更新当前每半小时发生的事件。)
问题:
这个计时器工作得很好,但偶尔会关闭 1 秒并不断重置。
示例:
运行 如预期:
Tick 29: 22
Running every 1 second
Tick 29: 23
Running every 1 second
Tick 29: 24
Running every 1 second
Tick 29: 25
Running every 5 seconds
Tick 29: 30
Running every 30 seconds
Tick 30: 0
Running every 30 minutes
Tick 0: 0
Running every 30 minutes
Insanity(随机下车一秒)
Running every 1 second
Tick 30: 53
Running every 1 second
Tick 30: 55
Running every 5 seconds
Tick 31: 1
Running every 1 second
Tick 31: 3
Running every 1 second
Tick 31: 5
Running every 5 seconds
Tick 31: 11
Running every 1 second
Tick 31: 13
Running every 1 second
Tick 31: 15
Running every 5 seconds
Tick 31: 21
Running every 1 second
Tick 31: 22
Running every 1 second
Tick 31: 24
Running every 1 second
Tick 31: 26
Running every 1 second
Tick 31: 28
Running every 1 second
Tick 31: 30
Running every 30 seconds
我的问题:
为什么?为什么它似乎随机下车一秒钟,我该如何防止这种情况发生?有没有更好的方法来实现这一目标?我怀疑这可能与 Date().getSeconds()
有关
代码:
在 CodePen 上:http://codepen.io/StuffieStephie/pen/RKRVXe
/* Description: A dynamic timer function to run every XX:00:00 or XX:30:00 in as few executions as possible */
var one_sec = 1000; // One second is 1,000 milliseconds
var one_min = one_sec * 60; // Times 60 is a minute
var intervalms = one_sec; // This is how often we'll run the function | Every 1s at first
//Some variables so we can output it to the screen instead of console
var messageHolder = document.getElementById("messageHolder");
var m = one_sec; // MSG: What unit of time we're measuring in
var mString = " second"; // MSG: String value of time measument
function tick() {
clearInterval(interval); //Clear the timer so we can start with new dynamic value.
var secs = new Date().getSeconds(); //get the second mark of the current time
var mins = new Date().getMinutes(); //get the minute mark of the current time
if (secs == "00"){ // If the value of secs is currently 0 (new minute XX:XX:00)
intervalms = one_min; // Set the interval every minute
m = one_min;
mString = " minute"
// Internal if: New minute AND
// Minute mark is at XX:00:00 OR XX:30:00
if (mins == "00" || mins == "30") {intervalms = 30 * one_min; mString =" minutes"; } //set every half-hour
//Otherwise: if Minute mark is divisible by 10 (like 'XX:10:00', 'XX:20:00', etc)
else if (mins % 10 == 0) {intervalms = 10 * one_min; mString =" minutes";} //set every 10 minutes
//Otherwise: if Minute mark is divisible by 5 (like 'XX:05:00', 'XX:10:00', etc)
else if (mins % 5 == 0) {intervalms = 5 * one_min; mString =" minutes";} //set every 5 minutes
} // END if (new minute XX:XX:00)
// Original if: Not a new minute
//Otherwise: if the value of secs is currently 30 (new minute XX:XX:30)
else if (secs == "30") {intervalms = 30 * one_sec; mString = " seconds";} //set every 30 seconds
//Otherwise: if second mark is divisible by 10 (like 'XX:10:00', 'XX:20:00', etc)
else if (secs % 10 == 0 ) {intervalms = 10 * one_sec; mString = " seconds";} //set every 10 seconds
//Otherwise: if second mark is divisible by 5 (like 'XX:05:00', 'XX:10:00', etc)
else if (secs % 5 == 0 ) {intervalms = 5 * one_sec; mString = " seconds";} //set every 5 seconds
// if none of the above things are true `intervalms` is never reassigned and the function runs every second
// EDIT: Sanity Check: Apparently it can get off so reset to 1s if needed
else {intervalms = one_sec; mString = " second"; m = one_sec;}
// Logging to console
console.log('Tick ' + mins + ': ' + secs); //Output stuff to the console so we can see it
console.log('Running every ' + (intervalms/m) + mString);
// Appending content in the html
msg = document.createElement("div");
msg.innerHTML = 'Tick ' + mins + ': ' + secs +'<br>';
msg.innerHTML += 'Running every ' + (intervalms/m) + mString;
messageHolder.appendChild(msg);
interval = setInterval(tick, intervalms);
}
var interval = setInterval(tick, intervalms);
<h1>A simple progrssive timer</h1>
<p>A dynamic timer function to run every XX:00:00 or XX:30:00 in as few executions as possible. </p>
<p>This timer can apparently get off at random?</p>
<div id="messageHolder"> </div>
不是答案,只是建议。你的 if else 东西真的很长。可以这样做:
function ticker(times,callback,i=0){
setTimeout(function(){
callback();
ticker(times,callback,i+1);
},times[i]);
}
这样使用:
ticker([5,10,20,30,3000],function(){
alert(new Date.getMinutes());
});
它将在5,10...秒后执行代码
或者如果你想执行半小时:
function allhalfhour(func){
var time=30-(new Date().getMinutes()%30);//time missing to a multiple of a half hour (30 or 60);
setTimeout(function(){
func();
setInterval(func,60*30);
},60*time);
}
这样使用:
allhalfour(function(){
alert(new Date.getMinutes());
});
setTimeout 和 setInterval 都会在超过几秒的时间内漂移很多,因此最好 运行 更频繁地使用计时器,然后仅在必要时调用该函数。
下面使用一般方程计算所需的延迟:
延迟到下一个运行 = 周期长度 - 自上一周期结束以来的时间 + 缓冲区
以下计算到下一个偶数周期的时间并适应可能具有 15 或 30 分钟分量的时区偏移(所有值均为毫秒):
var now = new Date();
var lengthOfPeriod = 30 * 60 * 1000; // or just 1.8e6
var timeSinceEndOfLast = (now.getMinutes() % 30) * 6e4 +
now.getSeconds() * 1000 +
now.getMilliseconds();
另一种方法是使用 Date.now 并通过本地时区偏移量调整值:
var now = new Date();
var lengthOfPeriod = 30 * 60 * 1000; // or just 1.8e6
var timeSinceEndOfLast = (Date.now() - now.getTimezoneOffset() * 6e4) % lengthOfPeriod;
我还添加了一个小缓冲区以确保它 运行 正好在句点之后。这应该能够适应任何偶数时间段,例如分钟、5 分钟、30 分钟、2 小时,等等。
function showTime() {
var clock = document.querySelectorAll('.clock');
var now = new Date();
clock[0].textContent = now.getHours();
clock[1].textContent = ('0' + now.getMinutes()).slice(-2);
clock[2].textContent = ('0' + now.getSeconds()).slice(-2);
}
/* Run provided function on each even multiple of period
** Function runs 90% of the way to the next period, or on period
** if delay is less than 1 second (1000 ms);
** @param {Function} fn - function to run
** @param {number} period - even period in ms (e.g. 60000 for one minute)
** @param {boolean} hold - don't run the first call (default is false)
*/
function doUpdate(fn, period, hold) {
var buffer = 20;
if (!hold) fn();
hold = false;
var now = new Date();
var delayToNext = period - ((Date.now() - now.getTimezoneOffset() * 6e4) % period);
if (delayToNext > 1000) {
delayToNext *= .9;
hold = true;
}
setTimeout(function(){doUpdate(fn, period, hold)}, delayToNext + buffer);
}
window.onload = function() {
doUpdate(showTime, 60000); // run each minute
}
.clock {
width: 2em;
text-align: center;
}
<table>
<tr><th>Hr<th>Min<th>Sec
<tr>
<td class="clock">
<td class="clock">
<td class="clock">
</table>
我有一个带有动态间隔的简单渐进式定时器功能。
目标:
让这个函数在尽可能少的执行中每 XX:00:00 或 XX:30:00 运行。它从每秒 运行ning 开始,然后每 5 秒、10 秒、30 秒等,直到每 30 分钟达到用户的 :30 分钟标记或新的小时标记。
(我最终将在 AngularJS 驱动的日程应用程序中使用它来更新当前每半小时发生的事件。)
问题:
这个计时器工作得很好,但偶尔会关闭 1 秒并不断重置。
示例:
运行 如预期:
Tick 29: 22
Running every 1 second
Tick 29: 23
Running every 1 second
Tick 29: 24
Running every 1 second
Tick 29: 25
Running every 5 seconds
Tick 29: 30
Running every 30 seconds
Tick 30: 0
Running every 30 minutes
Tick 0: 0
Running every 30 minutes
Insanity(随机下车一秒)
Running every 1 second
Tick 30: 53
Running every 1 second
Tick 30: 55
Running every 5 seconds
Tick 31: 1
Running every 1 second
Tick 31: 3
Running every 1 second
Tick 31: 5
Running every 5 seconds
Tick 31: 11
Running every 1 second
Tick 31: 13
Running every 1 second
Tick 31: 15
Running every 5 seconds
Tick 31: 21
Running every 1 second
Tick 31: 22
Running every 1 second
Tick 31: 24
Running every 1 second
Tick 31: 26
Running every 1 second
Tick 31: 28
Running every 1 second
Tick 31: 30
Running every 30 seconds
我的问题:
为什么?为什么它似乎随机下车一秒钟,我该如何防止这种情况发生?有没有更好的方法来实现这一目标?我怀疑这可能与 Date().getSeconds()
代码:
在 CodePen 上:http://codepen.io/StuffieStephie/pen/RKRVXe
/* Description: A dynamic timer function to run every XX:00:00 or XX:30:00 in as few executions as possible */
var one_sec = 1000; // One second is 1,000 milliseconds
var one_min = one_sec * 60; // Times 60 is a minute
var intervalms = one_sec; // This is how often we'll run the function | Every 1s at first
//Some variables so we can output it to the screen instead of console
var messageHolder = document.getElementById("messageHolder");
var m = one_sec; // MSG: What unit of time we're measuring in
var mString = " second"; // MSG: String value of time measument
function tick() {
clearInterval(interval); //Clear the timer so we can start with new dynamic value.
var secs = new Date().getSeconds(); //get the second mark of the current time
var mins = new Date().getMinutes(); //get the minute mark of the current time
if (secs == "00"){ // If the value of secs is currently 0 (new minute XX:XX:00)
intervalms = one_min; // Set the interval every minute
m = one_min;
mString = " minute"
// Internal if: New minute AND
// Minute mark is at XX:00:00 OR XX:30:00
if (mins == "00" || mins == "30") {intervalms = 30 * one_min; mString =" minutes"; } //set every half-hour
//Otherwise: if Minute mark is divisible by 10 (like 'XX:10:00', 'XX:20:00', etc)
else if (mins % 10 == 0) {intervalms = 10 * one_min; mString =" minutes";} //set every 10 minutes
//Otherwise: if Minute mark is divisible by 5 (like 'XX:05:00', 'XX:10:00', etc)
else if (mins % 5 == 0) {intervalms = 5 * one_min; mString =" minutes";} //set every 5 minutes
} // END if (new minute XX:XX:00)
// Original if: Not a new minute
//Otherwise: if the value of secs is currently 30 (new minute XX:XX:30)
else if (secs == "30") {intervalms = 30 * one_sec; mString = " seconds";} //set every 30 seconds
//Otherwise: if second mark is divisible by 10 (like 'XX:10:00', 'XX:20:00', etc)
else if (secs % 10 == 0 ) {intervalms = 10 * one_sec; mString = " seconds";} //set every 10 seconds
//Otherwise: if second mark is divisible by 5 (like 'XX:05:00', 'XX:10:00', etc)
else if (secs % 5 == 0 ) {intervalms = 5 * one_sec; mString = " seconds";} //set every 5 seconds
// if none of the above things are true `intervalms` is never reassigned and the function runs every second
// EDIT: Sanity Check: Apparently it can get off so reset to 1s if needed
else {intervalms = one_sec; mString = " second"; m = one_sec;}
// Logging to console
console.log('Tick ' + mins + ': ' + secs); //Output stuff to the console so we can see it
console.log('Running every ' + (intervalms/m) + mString);
// Appending content in the html
msg = document.createElement("div");
msg.innerHTML = 'Tick ' + mins + ': ' + secs +'<br>';
msg.innerHTML += 'Running every ' + (intervalms/m) + mString;
messageHolder.appendChild(msg);
interval = setInterval(tick, intervalms);
}
var interval = setInterval(tick, intervalms);
<h1>A simple progrssive timer</h1>
<p>A dynamic timer function to run every XX:00:00 or XX:30:00 in as few executions as possible. </p>
<p>This timer can apparently get off at random?</p>
<div id="messageHolder"> </div>
不是答案,只是建议。你的 if else 东西真的很长。可以这样做:
function ticker(times,callback,i=0){
setTimeout(function(){
callback();
ticker(times,callback,i+1);
},times[i]);
}
这样使用:
ticker([5,10,20,30,3000],function(){
alert(new Date.getMinutes());
});
它将在5,10...秒后执行代码
或者如果你想执行半小时:
function allhalfhour(func){
var time=30-(new Date().getMinutes()%30);//time missing to a multiple of a half hour (30 or 60);
setTimeout(function(){
func();
setInterval(func,60*30);
},60*time);
}
这样使用:
allhalfour(function(){
alert(new Date.getMinutes());
});
setTimeout 和 setInterval 都会在超过几秒的时间内漂移很多,因此最好 运行 更频繁地使用计时器,然后仅在必要时调用该函数。
下面使用一般方程计算所需的延迟:
延迟到下一个运行 = 周期长度 - 自上一周期结束以来的时间 + 缓冲区
以下计算到下一个偶数周期的时间并适应可能具有 15 或 30 分钟分量的时区偏移(所有值均为毫秒):
var now = new Date();
var lengthOfPeriod = 30 * 60 * 1000; // or just 1.8e6
var timeSinceEndOfLast = (now.getMinutes() % 30) * 6e4 +
now.getSeconds() * 1000 +
now.getMilliseconds();
另一种方法是使用 Date.now 并通过本地时区偏移量调整值:
var now = new Date();
var lengthOfPeriod = 30 * 60 * 1000; // or just 1.8e6
var timeSinceEndOfLast = (Date.now() - now.getTimezoneOffset() * 6e4) % lengthOfPeriod;
我还添加了一个小缓冲区以确保它 运行 正好在句点之后。这应该能够适应任何偶数时间段,例如分钟、5 分钟、30 分钟、2 小时,等等。
function showTime() {
var clock = document.querySelectorAll('.clock');
var now = new Date();
clock[0].textContent = now.getHours();
clock[1].textContent = ('0' + now.getMinutes()).slice(-2);
clock[2].textContent = ('0' + now.getSeconds()).slice(-2);
}
/* Run provided function on each even multiple of period
** Function runs 90% of the way to the next period, or on period
** if delay is less than 1 second (1000 ms);
** @param {Function} fn - function to run
** @param {number} period - even period in ms (e.g. 60000 for one minute)
** @param {boolean} hold - don't run the first call (default is false)
*/
function doUpdate(fn, period, hold) {
var buffer = 20;
if (!hold) fn();
hold = false;
var now = new Date();
var delayToNext = period - ((Date.now() - now.getTimezoneOffset() * 6e4) % period);
if (delayToNext > 1000) {
delayToNext *= .9;
hold = true;
}
setTimeout(function(){doUpdate(fn, period, hold)}, delayToNext + buffer);
}
window.onload = function() {
doUpdate(showTime, 60000); // run each minute
}
.clock {
width: 2em;
text-align: center;
}
<table>
<tr><th>Hr<th>Min<th>Sec
<tr>
<td class="clock">
<td class="clock">
<td class="clock">
</table>