一直困扰我的是setTimeout()
Javascript中的方法多么不可预测。
以我的经验,计时器在很多情况下都非常不准确。所谓不准确,是指实际延迟时间或多或少地相差250-500ms。尽管这不是很长的时间,但是使用它来隐藏/显示UI元素时,时间显然是显而易见的。
是否有任何技巧可以确保setTimeout()
准确执行(无需借助外部API),或者这是一个失败的原因?
Answers:
是否有任何技巧可以确保
setTimeout()
准确执行(无需借助外部API),或者这是一个失败的原因?
不,不。您不会通过它获得任何接近完美准确的计时器的功能setTimeout()
-尚未为此设置浏览器。但是,您也不需要依靠它来计时。多数动画库是在几年前弄清楚的:您使用设置了回调setTimeout()
,但根据的值确定需要执行的操作(new Date()).milliseconds
(或等效)。这使您可以在较新的浏览器中利用更可靠的计时器支持,同时在较旧的浏览器上仍可正常运行。
它还可以避免使用太多计时器!这很重要:每个计时器都是一个回调。每个回调都执行JS代码。在执行JS代码时,浏览器事件(包括其他回调)将被延迟或丢弃。回调完成后,其他回调必须与其他浏览器事件竞争才能执行。因此,一个在该时间间隔内处理所有待处理任务的计时器要比两个时间间隔一致的计时器要好,并且(对于短超时)要比两个重叠超时的计时器要好!
摘要:停止使用setTimeout()
实现“一个计时器/一个任务”的设计,并使用实时时钟来平滑UI动作。
。
REF; http://www.sitepoint.com/creating-accurate-timers-in-javascript/
这个网站在很大程度上帮助了我。
您可以使用系统时钟来补偿计时器的不准确性。如果您将计时功能作为一系列setTimeout调用(每个实例都调用下一个实例)来运行,则要确保其准确性,您要做的就是精确地确定其准确性,并从下一次迭代中减去该差值:
var start = new Date().getTime(),
time = 0,
elapsed = '0.0';
function instance()
{
time += 100;
elapsed = Math.floor(time / 100) / 10;
if(Math.round(elapsed) == elapsed) { elapsed += '.0'; }
document.title = elapsed;
var diff = (new Date().getTime() - start) - time;
window.setTimeout(instance, (100 - diff));
}
window.setTimeout(instance, 100);
这种方法将使漂移最小化,并将不准确度降低90%以上。
它解决了我的问题,希望对您有所帮助
elapsed
在代码中需要变量?
doTimer
那篇文章结尾的功能正是我所需要的。超级准确。
我不久前遇到了类似的问题,并提出了一种requestAnimationFrame
与performance.now()相结合的方法,该方法非常有效。
我现在可以使计时器精确到小数点后12位:
window.performance = window.performance || {};
performance.now = (function() {
return performance.now ||
performance.mozNow ||
performance.msNow ||
performance.oNow ||
performance.webkitNow ||
function() {
//Doh! Crap browser!
return new Date().getTime();
};
})();
如果您需要在给定的时间间隔内获得准确的回调,此要点可能会帮助您:
如果您习惯于setTimeout()
快速让浏览器生成内容,以便它的UI线程可以赶上它需要完成的任何任务(例如更新选项卡,或者不显示“长时间运行的脚本”对话框),那么有一个名为Efficient的新API脚本Yielding,又名,setImmediate()
可能对您更好。
setImmediate()
与的操作非常相似setTimeout()
,但如果浏览器没有其他操作,它可能会立即运行。在许多使用setTimeout(..., 16)
or或setTimeout(..., 4)
or的情况下setTimeout(..., 0)
(例如,您希望浏览器运行任何未完成的UI线程任务并且不显示“长时间运行的脚本”对话框),只需将您的替换setTimeout()
为setImmediate()
,并删除第二个(毫秒)参数即可。
与之不同的setImmediate()
是,它基本上是产量。如果浏览器有时间在UI线程上执行操作(例如,更新选项卡),它将在返回回调之前执行此操作。但是,如果浏览器已经完成了所有工作,则在setImmediate()
将基本上没有延迟地运行。
不幸的是,目前只有IE9 +支持此功能,因为有些支持其他浏览器供应商。
但是,如果您想使用它,并希望其他浏览器在某个时候实现它,则可以使用一个很好的polyfill。
如果setTimeout()
用于动画,请最好选择requestAnimationFrame,因为代码将与监视器的刷新率同步运行。
如果您使用setTimeout()
较慢的节奏(例如每300毫秒一次),则可以使用类似于user1213320建议的解决方案,在该解决方案中,您监视自上次时间戳开始运行了多长时间,并补偿了任何延迟。一项改进是,您可以使用新的“高分辨率时间”界面(aka window.performance.now()
),而不是Date.now()
在当前时间获得大于毫秒的分辨率。
您需要“提高”目标时间。某些尝试和错误将是必要的,但本质上是这样。
设置超时时间以在所需时间之前100毫秒完成
使超时处理程序函数如下所示:
calculate_remaining_time
if remaining_time > 20ms // maybe as much as 50
re-queue the handler for 10ms time
else
{
while( remaining_time > 0 ) calculate_remaining_time;
do_your_thing();
re-queue the handler for 100ms before the next required time
}
但是您的while循环仍然会被其他进程打断,因此仍然不够完美。
这是演示Shog9的建议的示例。这会在6秒钟内顺利填充jquery进度栏,然后在填充后重定向到另一个页面:
var TOTAL_SEC = 6;
var FRAMES_PER_SEC = 60;
var percent = 0;
var startTime = new Date().getTime();
setTimeout(updateProgress, 1000 / FRAMES_PER_SEC);
function updateProgress() {
var currentTime = new Date().getTime();
// 1000 to convert to milliseconds, and 100 to convert to percentage
percent = (currentTime - startTime) / (TOTAL_SEC * 1000) * 100;
$("#progressbar").progressbar({ value: percent });
if (percent >= 100) {
window.location = "newLocation.html";
} else {
setTimeout(updateProgress, 1000 / FRAMES_PER_SEC);
}
}
setTimeout()
。我将进行相应的编辑。
这是我为我的音乐项目制作的一个计时器,用于执行此操作。在所有设备上都准确的计时器。
var Timer = function(){
var framebuffer = 0,
var msSinceInitialized = 0,
var timer = this;
var timeAtLastInterval = new Date().getTime();
setInterval(function(){
var frametime = new Date().getTime();
var timeElapsed = frametime - timeAtLastInterval;
msSinceInitialized += timeElapsed;
timeAtLastInterval = frametime;
},1);
this.setInterval = function(callback,timeout,arguments) {
var timeStarted = msSinceInitialized;
var interval = setInterval(function(){
var totaltimepassed = msSinceInitialized - timeStarted;
if (totaltimepassed >= timeout) {
callback(arguments);
timeStarted = msSinceInitialized;
}
},1);
return interval;
}
}
var timer = new Timer();
timer.setInterval(function(){console.log("This timer will not drift."),1000}
讨厌这么说,但我认为没有办法缓解这种情况。我认为这取决于客户端系统上,不过,这样更快的JavaScript引擎或机器可能使其稍微更准确。
这里肯定有一个限制。为了给您一些视角,Google刚刚发布的Chrome浏览器足够快,可以在15-20毫秒内执行setTimeout(function(){},0),而较早的Javascript引擎执行该功能要花费数百毫秒的时间。尽管setTimeout使用毫秒,但是目前没有JavaScript虚拟机可以执行这种精度的代码。
Dan,根据我的经验(包括在JavaScript中实现SMIL2.1语言的实现,其中涉及到时间管理),我可以向您保证,您实际上不需要setTimeout或setInterval的高精度。
但是,重要的是在排队时执行setTimeout / setInterval的顺序-始终可以完美运行。
JavaScript超时的实际限制为10到15毫秒(除非您实际执行185毫秒,否则我不确定您要做什么才能获得200毫秒)。这是由于Windows具有15ms的标准计时器分辨率,因此,做得更好的唯一方法是使用Windows的高分辨率计时器,这是系统范围的设置,因此可以与系统上的其他应用程序连接,并且可以减少电池寿命(Chrome英特尔在此问题上的错误)。
实际的10-15毫秒标准是由于人们在网站上使用0毫秒超时,然后以一种假设10 -15毫秒超时的方式进行编码(例如,js游戏假设60 fps,但要求0毫秒/帧,没有增量逻辑,因此游戏/网站/动画的速度要比预期快几个数量级)。为此,即使在没有Windows计时器问题的平台上,浏览器也将计时器分辨率限制为10ms。
这是我用的。由于是JavaScript,因此我将同时发布Frontend和node.js解决方案:
对于这两种方法,我都建议使用相同的十进制舍入函数,我极力建议您保持十足的距离,原因如下:
const round = (places, number) => +(Math.round(number + `e+${places}`) + `e-${places}`)
places
-四舍五入的小数位数应该是安全的,并且应该避免浮点数的任何问题(某些数字,例如1.0000000000005〜可能是有问题的)。我花了一些时间研究舍入由高分辨率计时器提供的十进制数的最佳方法,该计时器已转换为毫秒。
that + symbol
-这是一元运算符,可将操作数转换为数字,实际上与 Number()
浏览器
const start = performance.now()
// I wonder how long this comment takes to parse
const end = performance.now()
const result = (end - start) + ' ms'
const adjusted = round(2, result) // see above rounding function
node.js
// Start timer
const startTimer = () => process.hrtime()
// End timer
const endTimer = (time) => {
const diff = process.hrtime(time)
const NS_PER_SEC = 1e9
const result = (diff[0] * NS_PER_SEC + diff[1])
const elapsed = Math.round((result * 0.0000010))
return elapsed
}
// This end timer converts the number from nanoseconds into milliseconds;
// you can find the nanosecond version if you need some seriously high-resolution timers.
const start = startTimer()
// I wonder how long this comment takes to parse
const end = endTimer(start)
console.log(end + ' ms')
您可以考虑使用html5网络音频时钟,该时钟使用系统时间来提高准确性