setTimeout неточност #2
Отново е неделя, ден за почивка, и аз отново ще ви занимавам с ECMAScript. Преди време бях писал по въпроса, но не предполагах, че проблема може да се окаже и по-сериозен. Става въпрос за следния тест:
function testInterval()
{
var interval = 20;
var timesRepeat = 500;
var count = 0;
function tick()
{
count ++;
}
tick(); // setInterval does not do an immediate call;
var ip = setInterval(tick, interval);
var startDate = (new Date()).getTime();
// JavaScript/ActionScript switch;
var out = alert || trace;
var test = function ()
{
clearInterval(ip);
var ranfor = \"ran for \" + ((new Date()).getTime() - startDate) + \" milliseconds. \";
var testResult = \"tick executed \" + count + \" times; expected \" + timesRepeat;
out(ranfor + testResult);
}
setTimeout(test, timesRepeat * interval);
}
testInterval();
Изглежда сравнително верен, макар и пълен с лоши практики. Сигурно и тече.
IE, Opera, и flash плеъра пропадат доста гнусно -
ran for 10000 milliseconds. tick executed 321 times; expected 500
ran for 10016 milliseconds. tick executed 321 times; expected 500
ran for 10031 milliseconds. tick executed 419 times; expected 500
Mozilla Firefox се справя, но ако смъкнеш интервала на 10, проблемите почват да излизат и там.
Мога да се сетя няколко сценария, в които това може да те издъни; синхронизация на анимация със звук например. Добрата новина е, че си имам тест, спрямо който евентуално ще измисля нещо.
Ето и какво измислих -
function intervalTicker (interval, callback)
{
this.interval = interval;
this.callback = callback;
}
intervalTicker.prototype.getTime = function ()
{
return (new Date()).getTime();
}
intervalTicker.prototype.start = function ()
{
this.left = 0;
this.moment = this.getTime();
var instance = this;
var closure = function ()
{
instance.onTick();
}
this.ip = setInterval(closure, 1);
}
intervalTicker.prototype.onTick = function ()
{
var now = this.getTime();
this.left += now - this.moment;
this.moment = now;
while (this.left > this.interval)
{
this.callback.call(null);
this.left -= this.interval;
}
}
intervalTicker.prototype.stop = function ()
{
clearInterval(this.ip);
}
Това е кода, Eто го и теста:
function testTicker()
{
var interval = 20;
var timesRepeat = 500;
var count = 0;
function tick()
{
count ++;
}
tick(); // setInterval does not do an immediate call;
var ticker = new intervalTicker(interval, tick);
ticker.start();
var startDate = (new Date()).getTime();
// JavaScript/ActionScript switch;
var out = alert || trace;
var test = function ()
{
ticker.stop();
var runfor = \"ran for \" + ((new Date()).getTime() - startDate) + \" milliseconds. \";
var testResult = \"tick executed \" + count + \" times; expected \" + timesRepeat;
out(runfor + testResult);
}
setTimeout(test, timesRepeat * interval);
}
testTicker();
Изглежда културно, и поне го изпълнява очакваното количество пъти.
Октомври 2nd, 2006 at 8:37 am
Ама аз си мисля, че отклонението не идва от грешка в таймера, а от времето за обработка на останалата част от цикъла. Както биха казали физиците - нямаш “идеални физически условия” в експеримента и затова получаваш отклонение. Мисълта ми е, че няма как да го корегираш, защото вероятно зависи от хардуера, а не от софтуера! Пробва ли това да го завъртиш в един и същ браузър на 300Mhz и 64 РАМ и на 3Ghz и 2G РАМ? Мисля, че пак ще получиш различни резултати!?
Октомври 2nd, 2006 at 8:38 am
Иначе всички знаем как ECMA дели цяло четно на цяло четно и получава дробно нечетно
Това може да те издъни не по-малко… особено ако разчиташ на float числа, а не на цели.
Октомври 2nd, 2006 at 10:26 am
Не разполагам с гореупоменатата конфигурация - но може и от това да е. инче в цикъла нищо няма ве
. Само едно инкрементиране на променлива?