Для этой конкретной цели вам нужен самосинхронизирующийся цикл синхронизации.Основная идея состоит в том, чтобы не использовать setInterval()
, а рассчитывать каждый раз, сколько миллисекунд спустя вы хотите, чтобы цикл запускался в следующий раз, и использовать setTimeout()
для ожидания до тех пор.
Вот базовый пример:
function oncePerSecond(callback) {
var timerFunc = function () {
// get the current time rounded down to a whole second (with a 10% margin)
var now = 1000 * Math.floor(Date.now() / 1000 + 0.1);
// run the callback
callback(now);
// wait for the next whole second
setTimeout(timerFunc, now + 1000 - Date.now());
};
timerFunc();
}
// create a demo timer
oncePerSecond(function (now) {
document.getElementById('local').textContent = new Date(now).toString();
});
// add an artificial 0.5 second delay for the second timer
setTimeout(function () {
oncePerSecond(function (now) {
document.getElementById('utc').textContent = new Date(now).toUTCString();
});
}, 500);
<p>The local time is now: <span id="local">...</span></p>
<p>The UTC time is now: <span id="utc">...</span></p>
Обратите внимание, что хотя два таймера в приведенном выше фрагменте запускаются с интервалом в полсекунды, они синхронизируются сразу после первого обновления.
Причина странного * fudge-фактора + 0.1
в Math.floor(Date.now() / 1000 + 0.1)
заключается в том, что нет никакой гарантии, что таймер иногда не срабатывает рано, всего за несколько миллисекунд до того, как часы остановятся.Смещение в 0,1 секунды гарантирует, что мы будем округлять текущее время вперед в таких случаях, но мы все равно будем обычно округлять время вниз при первом обновлении (или после возможных неожиданных задержек).
Еще лучшеВ результате вы можете комбинировать эту технику с requestAnimationFrame()
, чтобы ваши таймеры не запускались без необходимости, пока пользователь, например, просматривает другую вкладку:
function oncePerSecondAnim(callback) {
var frameFunc = function () {
// get the current time rounded down to a whole second (with a 10% margin)
var now = 1000 * Math.floor(Date.now() / 1000 + 0.1);
// run the callback
callback(now);
// wait for the next whole second
setTimeout(timerFunc, now + 1000 - Date.now());
}, timerFunc = function () {
requestAnimationFrame(frameFunc);
};
timerFunc();
}
// create a demo timer
oncePerSecondAnim(function (now) {
document.getElementById('local').textContent = new Date(now).toString();
});
// add an artificial 0.5 second delay for the second timer
setTimeout(function () {
oncePerSecondAnim(function (now) {
document.getElementById('utc').textContent = new Date(now).toUTCString();
});
}, 500);
<p>The local time is now: <span id="local">...</span></p>
<p>The UTC time is now: <span id="utc">...</span></p>