I've faced the same problem a couple of times lately where I want something to happen on a particular moment in time. For example fire a midi note every quarter time, etc. Usually these get programmed by checking in every iteration of a loop something like this. In an event driven javascript world this would look like this:
on('tick', check);
function check(time) {
if (is_it_time(time)) {
do_your_thing();
}
}
Of course this if test gets executed way more often than is actually needed. I'm sure this is a common problem and I'm looking for a pattern or pointers to theory on this problem.
Edit: Should point out that this has nothing to do with actual time. The ticks I am receiving come not from the javascript real time clock. Maybe it would be interesting to hear how setTimeOut works under the hood. I can't imagine there is a if test every millisecond.
Edit2: The best thing I've come up with so far is hold a queue of functions for every every tick and cycle through them.
var tasks = new Array(96);
function check(ticks) {
tasks[ticks].forEach(function(task) {
task();
});
}
Since the number of tasks could grow large this is the most efficient I think.
function runAt(time, task) {
var now = new Date();
var delay = time - now;
return setTimeout(task, delay);
}
runAt(Date.parse("2015-05-29T00:00:00+00:00"), function() {
console.log("yo");
});
Related
I'm trying to make a clock for a Libet task - a cognitive task used by psychologists. By convention these clocks take 2560 ms to complete a revolution. Mine seems to be running quite a lot slower and I can figure out why.
I have made an example of the issue here: https://jsfiddle.net/Sumoza/re4v7Lcj/8/
On each iteration of a setInterval with a 1ms delay, I increase the angle of rotation of the hand by (Math.PI*2)/2560, i.e. 1/2560th of a circle in radians. So, incrementing one of these each ms should complete the circle in that number of ms. As you can see from the clock, this runs a fair bit slower. Can anyone shed any light as to why. Interestingly, *10 seems to look a lot closer to what I want - but that doesn't make much sense to me so I'm wary of the fix. Relevant JS from the fiddle below. Thanks for any help.
var time = 0;
var rad_tick = (Math.PI*2)/2560; //radians per tick: last number is ms per revolution
// Draw Clock
const clock = document.getElementById('clock')
const clock_ctx = clock.getContext('2d')
clock_ctx.translate(clock.width/2, clock.height/2);
radius = (clock.height/2) * 0.7;
function drawClock() {
clock_ctx.clearRect(-clock.width/2, -clock.height/2, clock.width, clock.height);
clock.style.backgroundColor = 'transparent';
clock_ctx.beginPath();
clock_ctx.arc(0, 0, radius, 0 , 2 * Math.PI);
clock_ctx.stroke();
}
drawClock();
// Draw Hand
const hand = document.getElementById('hand')
const hand_ctx = hand.getContext('2d')
hand_ctx.translate(hand.width/2, hand.height/2);
function drawHand(time){
hand_ctx.clearRect(-hand.width/2, -hand.height/2, hand.width, hand.height);
hand_ctx.beginPath();
hand_ctx.moveTo(0,0);
hand_ctx.rotate(time);
hand_ctx.lineTo(0, -radius*0.9);
hand_ctx.stroke();
hand_ctx.rotate(-time);
}
function drawTime(){
time+=rad_tick;//*10
drawHand(time)
}
var intervalId = setInterval(drawTime, 1);
Try this
var starttime = new Date().getTime();
function drawTime() {
var elapsed = new Date().getTime() - starttime;
var time = elapsed % 2560;
time = time * rad_tick;
console.log(elapsed + "->" + time);
drawHand(time)
}
Base things on the current time, and don't depend on setInterval to fire at the exact time you set it to fire. If there are things in-queue at the time that setInterval is supposed to fire, it will do those things in-queue first and only then run the function you've wired to setIterval (causing the delays you see --which build up more and more over time). Learn about the event loop and requestAnimationFrame.
Instead, get the time (freshly) prior to doing something, and calculate how much time has passed since the start time. Base your animations on these calculations. This way, even if there are slight delays, your last action will always sync things to the way they're supposed to look right now.
When you do it this way, you might lose some frames (skipping things the engine didn't have time to do), but it will likely complete much closer to the expected completion time.
The bottom line is, if you tell the browser to do more than it can do in a particular amount of time, there will be delays. However, you can skip frames of an animation to make things complete much closer to the expected completion time.
You ask setInterval to call your callback every 1ms, but does it actually call it every 1ms?
Try this code:
let cnt = 0;
setInterval(() => cnt++, 1);
setTimeout(() => { console.log(cnt); }, 1000);
For me, it prints something between 230 and 235. So looks like the actual (average) minimum interval for me is a bit over 4ms.
Adding on to Stig, since javascript isn't super fast when it comes to these things, it won't be super accurate. Using something like getTime() will allow proper timing. How about you call getTime in your drawTime function, so that around every millisecond, it will check the actual time and use that value.
I am trying to make a sleep function in Javascript.
The function drawLinesToHtmlCanvas() is meant to draw random lines to a HTML canvas and the user is meant to be able to see the lines being drawn in real time.
For this example I use a delay of 500ms, but would like to be able to go to 1ms (or even less than 1ms resolution in the future)
Originally I followed a answer from this post: What is the JavaScript version of sleep()?
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function drawLinesToHtmlCanvas() {
// Get canvas and context here...
var drawSpeed = 500; // ms.
for (i=0; i<lines; i++) {
// Draw lines to canvas...
await sleep(drawSpeed);
}
}
And that worked very well (above). It was efficient, did not slow the browser down at all and let me have some control over the timing.
The issue was that setTimeout() cannot seem to go down to 1ms precision and this is something I require for this function.
So instead I tried my own approach as follows:
function sleep(ms) {
ms = parseInt(ms);
var now = new Date();
nowMs = now.valueOf();
var endMs = nowMs + ms;
while (endMs > nowMs) {
nowMs = new Date().valueOf();
}
return true;
}
function drawLinesToHtmlCanvas() {
// Get canvas and context here...
var drawSpeed = 500; // ms.
for (i=0; i<lines; i++) {
// Draw lines to canvas...
while (!sleep(drawSpeed));
}
}
This one is very slow, the while loop waiting for the right time uses up all the browsers resources, it's completely unusable. In addition as the function drawLinesToHtmlCanvas() is running, the lines are not being updated to the canvas element.
The promise solution with the setTimeout() was fantastic, it is just not precise enough for my requirements.
Can I make a promise that works similar to the first example? But instead of using setTimeout() it uses a similar algorithm to my Date() now vs end ms comparison, as that would be much more accurate?
The lines need to be able to be drawn down to 1 ms for now and have real time updates, the user needs to be able to see the lines being drawn to the canvas.
Even if setTimeout did work on such extremely small time frames this would probably not have worked out. When you use callbacks and/or promises you rely of JS runtime's event loop. This event loop only executes your callback as fast as it can. The architecture is going to impose lags that will become visible when you go below 1ms. The callback in setTimeout is not exactly executed after N ms passes. After N ms passes it only becomes eligible to be executed. And it gets invoked finally only when its turn comes on another event loop tick.
As for your second approach it does not exactly "use up resources". The thing is you no longer use event loop. But you must remember that JS is single-threaded. And because of it when JS-code executes non-stop it will not let user interact with UI at all. User can do something only between event callback executions. So don't ever use long running whiles in JS in browser unless you want to ruin user experience. Maybe unless you use Web workers because they will let you create new threads, but then you wouldn't be able to draw anything from there.
In general your approach to animation as "drawing something and then sleeping" is rather naive. Performant and smooth animations are what the video cards are made for although writing it in browser to efficiently utilize video card may be tricky. If you want to make animation in browser then you have to find specific browser function calls made specifically for animation on a Canvas or WebGL.
Maybe start here:
https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations
Also think about if you actually need so may frames per second. Over 1000fps? Can the monitor make it? What about performance impact?
This is my code:
var d = new Date();
function myFunction() {
ifNewDay();
}
function ifNewDay() {
var currentTime = d.toLocaleTimeString();
if(currentTime == "3:21:00:00 AM EDT"){
Logger.log("It is 3:21 PM");
} else {
ifNewDay();
}
}
I am trying to write code that will only continue once it hits a certain time, I'm sure there's a way easier way to do this, but I'm brand new. Thanks.
There's a limit on how deep you can recurse, and in you case, that's A LOT of calls in a short period.
When you call a function it gets added to the stack in memory, and there's a limit on how big the stack can be. You can read more here about how it works.
You could use sleep and call that function only every second or so, or at least half a second or something.
It seems like using a time driving trigger is what would work best. Have you looked into triggers?
https://developers.google.com/apps-script/guides/triggers/installable
I'm writing an application in Node.js that needs to schedule functions to be run at specific times. Hours or sometimes days in the future. Currently, I'm doing so with something similar to this:
const now = Date.now();
const later = getSomeFutureTimestamp();
setTimeout(function() {
// do something
}, later - now);
I'm wondering if setTimeout is the proper tool for this job or if there is a tool more suited for long intervals of time. One thing to note is I don't need great precision; if the job runs within a few minutes of the scheduled time, everything should still be fine.
setTimeout should be fine. It's only really delayed if there's blocking code running at the moment when it's meant to execute. So setTimeout is typically 20 milliseconds late. But since your margin is minutes, I don't think it'll be an issue.
However, I'd suggest storing the timestamp at which things should trigger, and just periodically check. That way you only have 1 timer running at any given time.
You also get to keep your timestamps as absolute timestamps - not as relative, "milliseconds in the future" values. That also lets you store them between server restarts, which you can't do as easily with relative times. With your code, the only record of a job being queued is that there's a timer running. If that timer disappears for any reason, you lose all record of a job having been scheduled.
Something like:
function checkForScheduledJobs() {
var now = Date.now(),
job;
// assuming here that the jobs array is sorted earliest to latest
while(jobs.length && jobs[0].timestamp < now) {
jobs.shift().callback();
}
setTimeout(checkForScheduledJobs, 60000); // check each minute
}
You just need to kick it off once. This could be done in an addScheduledJob function (which would also sort the jobs array after adding something to it, etc.)
The problem is as such:
In a js and asm.js based multiplayer game I've got two loops.
One handles the actual game ticks, like unit position, velocity and combat.
The other handles rendering of this world onto the canvas for the user to see.
What I'd like to happen is when the processor/GPU(they made those the same thing on some machines now, can't say I'm happy about that) gets encumbered too much the rendering loop should skip and thus stop changing the canvas. I.e. freezing the game screen in a lag pike.
Meanwhile the little processing power left is used to successfully complete the actual game tick preventing de-synchronisation with other game clients.
(It's an RTS-like game when it comes to load so the user input instead of positions of all objects are sent over the net).
Failing this the client would have to be kicked by the other clients or all clients would have to pause for him to reconnect and resync. i.e. bad bad bad!
A sloppy makeshift way to do this would probably be by using timestamps and terminate the graphic loop if it won't be complete by a certain time. One would presumably do this by determining max execution time for the packet types on the stack of the loop and immediately terminate the loop if the "time to execute value" of all packets together is too great to be dealt with within the resource capacity the timestamps are indicating by slowdown measurement. Hell, maybe that's radical but perhaps even skip-terminating the graphic loop when any slowdown is detected just to be sure to avoid desync.
So priorotizing one loop over another(both handling ticks) and making the second one skip if a shortage in resource is detected to ensure the first one always completes it's tick within each timeframe(10 ticks per second here).
Any possibilities or best practice methods you guys can inform me on?
EDIT: Please focus on the ability to measure availability of cpu resources and the skipping/termination for one tick of the graphic loop if these resources would not available enough to finish both loops (i.e. if the loops won't finish in the 100ms timeframe after which the next loop tick should already be firing, don't start/terminate the graphics loop).
One solution would be to use a web worker to do your world update loop and then the normal javascript loop to do the render. You would need to hand the state back and forthright o /from the web worker but the render loop would only draw on the updated data.
An advantage is that you could still have reactionary display code on the main ui loop.
This also have the advantage of the fact that the web worker could be using a different core and with multiple web workers you could use multiple extra cores
Fo the logical loop, i would take setInterval, and for the paint - requestAnimationFrame. And even more - the callback at requestAnimationFrame also receives a timestamp, so you can track timestamps and skip single frame if some lack appear.
the processor is able to handle other tasks while also rendering the animation
This statement is wrong - processor can handle only one task, and requestAnimationFrame is not actually the Rendering, it is your callback - generic javascript. You can think about it like a setTimeout. The only difference is, that it tries to run the callback on next free framerate's frame. That's why it is much better than setTimeout. So for the animations you must use the requestAnimationFrame. Other good part about it is, when the webpage is in background(other tab opened). Then the callback wont be called until it comes to the foreground. This saves processor time, as nothing is calculated in that callback.
Going back to your question: You now but, that only one callback can be processed in a time, so if the processor is in a particular time busy with the logical function, then the callback of the animation loop won't be fired. In that case it calls 'lag'. But as I understood, it is actually the desired behavior - to give the logical callback function more time. But there is other side. What if your animation function is busy, when the time for logical function came to be fired? In this case it will be fired only when animation function ends. There is nothing to do about it. If your animation function is 'heavy', you could only try to split it for 2 frames. One frame - prepare everything for render, the second one - render.
But anyway, you never become millisecond-perfect interval or timeout in javascript. As it want be called until event-loop is not free. To get the idea:
var seconds = 0;
setInterval(function(){ seconds++; var x = 10e8; while(--x); }, 1000);
Depends on you CPU, but after 10 seconds time, variable 'seconds' will be much less then 10.
And one more thing, if you really rely on time, then it is safer to use Date.now() to synchronize next logical tick:
var setLogicalLoop = (function(){
var _startedAt,
_stop,
_ms;
function frame(){
if (_stop === true)
return;
// calculations
var dt = Date.now() - _startedAt,
diff = dt % _ms;
setTimeout(frame, ms - diff);
};
return function (callback, ms){
_startedAt = Date.now();
_stop = false;
setTimeout(frame, ms);
return function(){
_stop = true;
};
};
});
// -> start
var stopLoop = setLogicalLoop(myFunction, ms);
// -> stop
stopLoop();