I'm running into some strange error with the Date.now() function in JS.
Background Info: I'm developing a webApp that sends and receives packets to/from a server. All my incoming packets are marked with a timestamp, generated by Date.now(). Among these packets is data, that is printed into a line chart. So I get a value on the y axis and the timestamp on the x axis. So far so good.
Letting the app run has shown me some strange behaviour -> My line chart sometimes draws data points back in the past, right before already present data points! While checking the timestamps of these points i saw, that my chart does everything right, but the timestamps are wrong.
So I wrote a little test script, to collect some more evidence:
var i = 0;
var ts = [Date.now()];
setInterval(function () {
ts.push(Date.now());
console.info("checking next timestamp ...");
if (ts[i] > ts[i+1]) {
console.error("old timestamp (" + ts[i] + ") is BIGGER than current timestamp (" + ts[i+1] + ")!");
}
i++;
}, 100);
Running this over a few minutes prints me one error:
Way later i get another one, and so on.
For this test i chose 100ms interval time to check many timestamps. But my App has an update interval of maybe 5s and still there arrives this error sometimes. Not as often as with 100ms, but it's there :(
Has anyone else seen this behaviour before? Am I doing something wrong here?
BTW: The packets in my App come in one by one, so it's impossible that the order of the packets is mixed up or sth.
Well, as I have taken a look onto the Performance API yesterday, I think this is the way to go. I implemented it in my application as follows:
Everytime when I receive an input packet from my server I give them a timestamp. Until now I had realised this by Date.now() but this seems to be inconsistent as the OS or the VM (or both) recalculate their current time from time to time.
So now I calculate my own timestamp. For this I have to read the timestamp from when my application started. The performance API gives me this value by
var startTS = window.performance.timing.navigationStart; //returns a timestamp in ms
So this is a value available everywhere in my app since it exists in the window namespace and does not need a global variable made by myself. Very nice.
Now, to get the current timestamp, I have to add a time value that indicates how long my application is running since startTS. To get this we just use:
var timeSinceStartup = window.performance.now(); //returns sth. like 1337.9836718 (ms with fractional part)
If needed, one can round this value as the fractional part might not be needed:
var rounded = (timeSinceStartup + 0.5) | 0; //same as but faster than Math.round(timeSinceStartup);
Well, the rest is easy. We just have to add those values (as both are milliseconds) and voila:
var currentTS = startTS + timeSinceStartup;
I have updated the little code snippet from my question to test this:
var i = 0;
var start = window.performance.timing.navigationStart;
var ts = [start + window.performance.now()];
setInterval(function () {
ts.push(start + window.performance.now());
console.info("checking next timestamp ...");
if (ts[i] > ts[i+1]) {
console.error("old timestamp (" + ts[i] + ") is BIGGER than current timestamp (" + ts[i+1] + ")!");
}
i++;
}, 100);
Chrome is running this test at the moment (in VM) and it looks like everything is just fine (17500 checks without an error). I would have wondered if sth. else would happen, as we just add a constant with a monoton rising value ;)
Only drawback of this solution seems to be the browser support. As of now every modern browser but Safari support this. As my app is developed to run only on modern browsers this is ok for me, but the lack of Safari is bad.
http://caniuse.com/#feat=high-resolution-time
I will have to take a look into crossbrowser solutions/polyfills/Frameworks or sth. Thanks everybody for your help. Anyway I'm still curious if there might be a solution for Date.now() in Chrome, as FF and IE had no problems with using it.
https://code.google.com/p/chromium/issues/detail?id=408077
EDIT
This seems to be a good polyfill for a missing Performance API:
https://gist.github.com/paulirish/5438650
Ofcourse it falls back to Date.now(), so in browsers that don't support the API I might run into the same problem as before. For this case I just keep checking my timestamps in the app and if a timing error occurs I'm going to handle it silently (e.g. don't draw it to the chart, instead wait for the next value).
So long...
Related
This question already has answers here:
How can I make setInterval also work when a tab is inactive in Chrome?
(16 answers)
Closed 5 years ago.
If I write a javascript code what uses setTimeout/setInterval, time will not be valid on Chrome if the related page isn't active.
To demonstrate, just simply create an html file with a js code like this:
<script>
var title = 1;
setInterval(function(){document.title = "X:"+title; title+=1}, 250);
</script>
Open page several time, and you'll see that if a page is not active, the title will increment only in abount 2 sec, instead of 250ms. It is a very critical issue in my development.
Does anyone know how to evade it? A simple Chrome options could be enought too if there is.
Just to mark it as not duplicate: It is not for animations, it is for background workings. The provided example is very accurate! I need to run script in very accurately and do operations fastly in backgrounded tabs. I know, 99,9999% of people does not need it...
Neither function is considered accurate by intention. You never should rely on provided timespans. This inaccuracy can be stressed to some amount by browser when your page isn't visible.
Actually it doesn't make sense to keep working in background using a regular webpage. If you really need to do that try some WebWorker instead. The webpage is for user interaction ... user interaction does not happen in background by intention. Processing things in background is best put in a worker thread. So, it's all about runtime context matching code's conceptual intention.
Adding to what cepharum has, it is impossible to guarantee that your code will be execute every 250ms exactly. Chrome is not a real time operating system, and neither is your code. Depending on the implementation of the interpreter, it may even experience drifts and delays.
With that being said, if you only want to reduce the delay, you can use a worker to trick the system by creating a new thread that will not have its refresh rate limited:
function createWorker(main){
var blob = new Blob(
["(" + main.toString() + ")(self)"],
{type: "text/javascript"}
);
return new Worker(window.URL.createObjectURL(blob));
}
// Worker
var worker = createWorker(function(self){
setInterval(function(){
self.postMessage(Date.now());
}, 250);
});
worker.onmessage = function(e) {
console.log("Worker: " + e.data);
}
https://jsfiddle.net/DerekL/ouzcdh9g/
I ran into the same issue before, the way I solved it was to use math to calculate the time difference, instead of rely on the setInterval being reliable. E.g.
var title = 1;
var tickTime = (new Date()).getTime();
setInterval(function(){
var now = (new Date()).getTime();
document.title = "X:"+title;
// you can adjust the math calculation below to
// meet your requirements
title+= (now - tickTime)/250;
tickTime = now;
}, 250);
Both of the server and client have the code
setInterval(function(){
player.x++;
player.y++;
}, 20);
I have a node.js / socket.io server where the player's x and y are being updated every frame and sent to the client. The client is also having a different x and y being updated using the same logic. The client seems to be moving faster than the server though so it's making the game not in sync.. What should I be doing differently?
that's a huge and kinda complex topic, I'm not in the mood to google these topics for you; but a few hints/directions.
This ain't meant to be a full answer, but it's more explanation, a comment can take.
1st: don't increment values, neither in the back- nor in the frontend.
Define them as an animation: { startTime, startValue, endTime, endValue }. Now you can interpolate the current position at any time, independant of the interval.
2nd: you have to account for the time-offset between front- and backend.
3rd: check in the frontend for manipulations of the timer: this is a common way to cheat. Don't take the correctness of the clock for granted.
4th: you also have to account for the ping, when recieving actions from the frontend for example, you should subtract the ping to know when the action has really been made. And don't forget to check the ping over and over again: it might change over time.
And again, you can only trust your own clock, and you should check for manipulations (eg. artificially slowing down the connection). google ping-cheater, ...
finally: You're not the first one to create a real-time game. Search for these topics, they have been solved, and they have been asked by other people in the past.
Search especially in the flash-community. Flash still is a popular gaming-platform, and AS3-code should be pretty easy to understand and to port to JS.
It was actually really really easy, anyone with the same problem just read this article. https://www.viget.com/articles/time-based-animation
The simplest way to put it "move based on how much time has passed since you've last moved" So just find a deltaTime variable by subtracting time_now from time_then.
This is what my updatePosition function looks like now
self.updatePosition = function(){
var now_time = (new Date).getTime();
var delta_time = now_time - self.then_time;
self.x += delta_time * self.velocityX;
self.y += delta_time * self.velocityY;
self.then_time = now_time;
}
Just set the then_time in your initialization or whenever your character can first start moving.
I am building an application that takes data from an android app and replays it in a browser. The android app basically allows the user to record a video and while it is recording it logs data every 100ms such as gps position, speed and accelerometer readings to a database. So i want the user to be able to play the video back in their browser and have charts, google map etc show a realtime representation of the data synced to the video. I have already achieved this functionality but it's far from perfect and I can't help thinking there must be a better way. What I am doing at the moment is getting all of the data from the database ordered by datetime ascending and outputting it as a json encoded array. Here is the process I am doing in pseudo code:
Use video event listener to find the current datetime of video
do a while loop from the current item in the data array
For each iteration check whether the datetime for that row is less than the current datetime from the video
If it is then update the dials from the data
Increment array key
Here is my code:
var points = <?php echo json_encode($gps); ?>;
var start_time = <?php echo $gps[0]->milli; ?>;
var current_time = start_time;
$(document).ready(function()
{
top_speed = 240;
min_angle = -210;
max_angle = 30;
total_angle = 0 - ((0-max_angle)+min_angle);
multiplier = top_speed / total_angle;
speed_i=0;
video.addEventListener('timeupdate', function() {
current_time = start_time + parseInt((video.currentTime * 1000).toFixed(0));
while(typeof points[speed_i] !== 'undefined' && current_time > points[speed_i].milli)
{
newpos = new google.maps.LatLng(points[speed_i].latitude, points[speed_i].longitude);
marker.setPosition(newpos);
map.setCenter(newpos);
angle = min_angle + (points[speed_i].speed * multiplier);
$("#needle").rotate({
animateTo : angle,
center: [13,11],
duration: 100
});
speed_i++;
}
}
});
Here are the issues I seem to have encountered:
- Have to load thousands of rows into json array which can't be very good for permorance
- Have to do while loop on every video call back - again can't be very good for performance
- Playback is always a bit behind
Can anyone think of any ways this can be improved or a better way completely to do it?
There are a few reasons why this may be running slowly. First, the timeupdate event only runs about every 250ms. So, if you're going to refresh at that rate, dandavis is right and you don't need that much data. But if you want animation that's that smooth, I suggest using requestAnimationFrame to update every 16ms or so.
As it is, if you update every 250ms, you're cycling through 2 or 3 data points and updating the map and needle three times in a row, which is unnecessary.
I recommend looking into Popcorn.js, which is built exactly for this kind of thing and will take care of this for you. It will also handle seeking or playing backwards. You'll want to pre-process the data so each point has a start time and an end time in the video.
There are also some things you can do to make the data transfer more efficient. Take out any extra properties that you don't need on every point. You can store each data point as an array, so the property names don't have to be included in your JSON blob, and then you can clean that up with a few lines of JS code on the client side.
Finally, separate your data file from your script. Save it as a static JSON file (maybe even gzipped if your server configuration can handle it) and fetch it with XMLHttpRequest. That way, you can at least display the page sooner while waiting for the code to download. Better yet, look into using a JSON streaming tool like Oboe.js to start displaying data points even before the whole file is loaded.
I created an animation using requestAnimationFrame. Works fine in Windows Chrome and IE; Safari (tested Safari 6 and 7) breaks. It turns out that rAF get a DOMHighResTimestamp rather than a Date timestamp. That's all fine and good, and what I expected, as it's now part of the spec. However, as far as I've been able to find, there's no way to get the current DOMHighResTimestamp (i.e. window.performance is not available, even with a prefix). So if I create the start time as a Date timestamp, it behaves radically wrong when I try to determine progress within the rAF callback (very small negative numbers).
If you look at this JSBin on Safari, it won't animate at all.
In this JBin, I've made a change to "skip" the first frame (where the time parameter is undefined), so that startTime gets set to the time parameter on the next frame. Seems to work, but skipping a frame seems a bit crappy.
Is there some way to get the current DOMHighResTimestamp in Safari, given the lack of window.performance? Or alternately, force rAF into some sort of legacy mode that forces it to get a Date timestamp instead?
Does anyone know why Safari has this inconsistency, where it provides the parameter in a format that you can't get at any other way?
Performance.now() is only a recommendation as of now. https://developer.mozilla.org/en-US/docs/Web/API/Performance.now() I can only assume it's a matter of time before it's official, seeing as how everyone but Safari has it built in.
Besides that fact use this to your advantage. Since you know requestAnimationFrame returns a DOMHighResTimestamp use that for your timing.
Game.start = function(timestamp){
if(timestamp){
this.time = timestamp;
requestAnimationFrame(Game.loop);
}else{
requestAnimationFrame(Game.start);
}
}
Game.loop = function(timestamp){
Game.tick(timestamp);
... your code
requestAnimationFrame(Game.loop);
}
Game.tick = function(timestamp) {
this.delta = timestamp - this.time;
this.time = timestamp;
};
What I do here, is call Game.start which will begin the loop (I've run into situations where the timestamp was undefined so we try until we get something valid). Once we get that we have our base time and since RAF is going to return a timestamp our tick function never has to call Date.now or performance.now as long as we pass it the timestamp returned by requestAnimationFrame.
My processor runs at 2.0 GHz.
I have a new computer with as much software removed as possible except my development tools. This system is clean with no malware.
When I run the code below I get about 2M loops per second. That is about 1 MHz.
Suppose doing and addition, and doing a compare takes 10x the simplest operation, I get about 10 MHz
Why do I not get more utilization of my processor?
var Utility =
{
time: function()
{
var end_time,
start_time,
index = 0;
start_time = new Date().getTime();
while ( index <= 1000000 )
{
index++;
}
end_time = new Date().getTime();
return ( end_time - start_time);
}
};
This is not really about JavaScript - it's more about the browser and the way it's handling JavaScript.
Each browser is doing this differently, but most modern browsers won't let JavaScript take 100% of the resources to prevent the client machine from crashing.
Bottom line you can't do such thing with client side scripting, you'll have to use "real" application with full access to the computer.
Suppose doing and addition, reading a clock, and doing a compare takes
100x the simplest operation (very conservative), I get (.1MHz * 100) =
1MHz.
This is not how computers work and measuring speed like this is not going to get you anywhere. Besides, it depends a lot on the JavaScript engine being used. I heard a lot of good things about the V8 JS engine that Chrome uses, Opera's seems to be pretty fast too.
So try it with different browsers to get a real comparison. But if you want to measure how much time it took to do some operation (pseudo code):
var start = get_current_time();
// do the complex operation
var end = get_current_time ();
var time_it_took = end - start;
The time functions should have as small a granularity as possible.
Well for one thing you would be creating a new date object every time thorugh the loop -- why not create the date object for comparision before you enter the loop?