Reputation: 2705
Consider the following code:
var tick = 0;
setInterval(() => {
console.log(tick);
}, 1000);
setInterval(() => {
tick += 1;
}, 1);
Basically, I want my game to look smooth, so I update the position of the dot every 1 millisecond, which the function in the second setInterval does.
In 1 second, or 1000 millisecond, I expect the dot to have moved 1000 pixels. However, it moves roughly 250 pixels.
When I run the following jsfiddle
, I see multiples of roughly 250 being printed to the console.
https://jsfiddle.net/58v74zw4/1/
I can fix it by multiplying 4, based on my observation, but it feels hacky.
What is causing this, and what is the correct fix for this?
Upvotes: 0
Views: 330
Reputation:
Best article I found about JS timers : https://johnresig.com/blog/how-javascript-timers-work.
Since JavaScript can only ever execute one piece of code at a time [...] when an asynchronous event occurs [...] it gets queued up to be executed later [...]
This means that if there is a big chunk of code that is executing for 4ms, the interval handler is queued up for at most 4ms.
ms | event
----|--------------------------------------------
0 | bigChunk() starts
1 |
2 | interval event fires
3 |
4 | bigChunk() returns
5 | interval handler starts
[...] browsers [...] wait until no more interval handlers are queued [...] before queuing more.
This means that if there is a big chunk of code that is executing for 4ms, and if the interval is set to fire every 1ms, the browser executes the first interval handler after 4ms and drops 3 events.
ms | event
----|--------------------------------------------
0 | bigChunk() starts
1 | interval event fires
2 | interval event dropped
3 | interval event dropped
4 | interval event dropped, bigChunk() returns
5 | interval handler starts
In your case, 3/4 (750/1000) of interval events may be dropped for this reason, but there are also implementation related causes, like this minimum delay of 4ms between successive calls described here : https://stackoverflow.com/a/9647221/1636522 (credits : LGSon).
As you can see, setInterval
is not reliable when used in a single thread, however, multi-threading does not necessarily make things easier. For example, if the execution time of your interval handler is 4ms, you will need 4 threads with a time shift of 1ms between each thread.
Thread 1 | Thread 2
-------------------------------|-------------------------------
ms | event | ms | event
----|--------------------------|----|--------------------------
0 | interval event fires | 0 |
1 | interval handler starts | 1 | interval event fires
2 | | 2 | interval handler starts
3 | | 3 |
4 | interval handler returns | 4 |
5 | | 5 | interval handler returns
And this is only one of the potential problems to solve. That said, if you still want to use multi-threading you could take a look at Web Workers. I can't help though, I've never used this feature :-| https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
Also keep in mind that "most browsers have 60fps [frame per second]" (see sunil), which means that you have almost 17ms (1000/60) to compute the next frame. Hence, updating the state of your game every millisecond is probably overkill :-)
Upvotes: 3