I created a simple function which counting value from 0 to some value during 3 sec.
Here is fiddle - / and snippet:
var number = document.querySelector('.number');
var button = document.querySelector('button');
button.addEventListener('click', function() {
counting(number, 2500);
})
function counting(elem, value) {
var count = 0;
var timerId = setInterval(function() {
if (++count == value) clearInterval(timerId);
elem.innerHTML = count;
}, 3000/value);
}
<span class="number">0</span>
<button>Go</button>
I created a simple function which counting value from 0 to some value during 3 sec.
Here is fiddle - https://jsfiddle/ar6akv5z/ and snippet:
var number = document.querySelector('.number');
var button = document.querySelector('button');
button.addEventListener('click', function() {
counting(number, 2500);
})
function counting(elem, value) {
var count = 0;
var timerId = setInterval(function() {
if (++count == value) clearInterval(timerId);
elem.innerHTML = count;
}, 3000/value);
}
<span class="number">0</span>
<button>Go</button>
But the duration of the function takes longer than 3 seconds. Can you explain me why it is happend or show me my mistake.
Thanks and sorry for my english
Share Improve this question edited Aug 12, 2016 at 21:27 T.J. Crowder 1.1m200 gold badges2k silver badges1.9k bronze badges asked Aug 12, 2016 at 21:24 HlushenokHlushenok 514 bronze badges 14-
++count
? Is this valid JavaScript? – yuriy636 Commented Aug 12, 2016 at 21:26 - 2 @yuriy636 yes - prefix increment – Rob M. Commented Aug 12, 2016 at 21:27
-
2
Technically, the
setTimeout
/setInterval
functions don't run something in exactly the timeout you give but rather schedule it to be run then. The difference is that since JS is single threaded, something else might be being processed already, so the timeout/interval-ed thing would be put on the queue – VLAZ Commented Aug 12, 2016 at 21:27 -
1
@Miloshio
querySelector
does not return an HTML Collection, it returns a single DOM node. – Rob M. Commented Aug 12, 2016 at 21:31 - 1 @T.J.Crowder I see, that's definitely short and accurate enough. Thanks for the tip, I'll make sure to spread the word :) – VLAZ Commented Aug 12, 2016 at 21:41
2 Answers
Reset to default 5There's a minimum delay enforced by setTimeout
and setInterval
. From MDN
Reasons for delays longer than specified
Nested timeouts forced to >=4ms
Historically browsers implement setTimeout() "clamping": successive setTimeout() calls with delay smaller than the "minimum delay" limit are forced to use at least the minimum delay. The minimum delay, DOM_MIN_TIMEOUT_VALUE, is 4 ms (stored in a preference in Firefox: dom.min_timeout_value), with a DOM_CLAMP_TIMEOUT_NESTING_LEVEL of 5.
In fact, 4 ms is specified by the HTML5 spec and is consistent across browsers released in 2010 and onward. Prior to (Firefox 5.0 / Thunderbird 5.0 / SeaMonkey 2.2), the minimum timeout value for nested timeouts was 10 ms.
So even though you're specifying 3000/2500 = 1.2
as the interval time, it's acting as if you'd used 4
as the interval time.
The minimum interval of a timer is subject to an algorithm specified is the HTML5 spec (originally it was in the now-defunct timers spec), to keep timers from firing too rapidly. When a timer schedules a timer (which is essentially what setInterval
does), once the nesting reaches five, if the timer interval requested is < 4ms, it's set to 4ms:
- If nesting level is greater than 5, and timeout is less than 4, then increase timeout to 4.
Since you're telling it to count up by 1 each time the timer fires, and it quite quickly starts only firing every 4ms (at best), it takes 4 * 2500 = 10000ms (10 seconds) to finish.
You can see this average delay in the updated snippet below, which replaces the counter with the average time between callbacks:
if (!Date.now) {
Date.now = function() {
return +new Date();
};
}
var number = document.querySelector('.number');
var button = document.querySelector('button');
var sum = 0;
var last = null;
button.addEventListener('click', function() {
counting(number, 2500);
})
function counting(elem, value) {
var count = 0;
last = Date.now();
var timerId = setInterval(function() {
var now = Date.now();
sum += now - last;
last = now;
if (++count == value) clearInterval(timerId);
elem.innerHTML = sum / count;
}, 3000/value);
}
<span class="number">0</span>
<button>Go</button>