JavaScript setTimeout Runs Twice - javascript

I am trying to make a function that starts in exact intervals to keep stanble update rate. The problem is that it seems to execute in 2 channels. This is the log:
timeElapsed=141; lastDraw=1314040922291
timeElapsed=860; lastDraw=1314040923151
timeElapsed=141; lastDraw=1314040923292
timeElapsed=860; lastDraw=1314040924152
timeElapsed=141; lastDraw=1314040924293
timeElapsed=860; lastDraw=1314040925153
timeElapsed=141; lastDraw=1314040925294
timeElapsed=860; lastDraw=1314040926154
timeElapsed=141; lastDraw=1314040926295
timeElapsed=859; lastDraw=1314040927154
timeElapsed=143; lastDraw=1314040927297
timeElapsed=858; lastDraw=1314040928155
timeElapsed=143; lastDraw=1314040928298
timeElapsed=858; lastDraw=1314040929156
timeElapsed=142; lastDraw=1314040929298
First, I exectute my function using
drawTimer = setTimeout(function(){ draw() }, 1);
and the function looks like this:
var draw = function(){
if(!running)
return;
var miliseconds = getCurrentMiliseconds();
var timeElapsed = miliseconds - lastDraw;
lastDraw = miliseconds;
console.log("timeElapsed=" + timeElapsed + "; lastDraw=" + lastDraw);
onDrawListener(timeElapsed);
if(timeElapsed < timeLapse)
miliseconds = timeLapse - timeElapsed;
else
miliseconds = 1;
drawTimer = setTimeout(function(){ draw() }, miliseconds);
}
It happens in both, Chrome and Firefox. Do you know what is it caused by? And... How to fix it?
P.S. Since everyone seems to be so confused about the running variable, here it is: it's a private parent object member that indicates whether the mechanism is still running or has stopped. It's set by other functions and is just there to make sure this function doesn't continue working after stop() is called.
-- update --
timeLapse is set to 1000 (1 time per seconds) and never changed again.
onDrawListener is set to this function:
function(timeElapsed){
canvas.clear();
moveSnake();
if(snake.body[0] == food){
food = getRandomFreePosition();
++snake.body.lenght;
}
drawBackground();
drawFood();
drawSnake();
}
to explain it: canvas is kinda the engine that takes care of callbacks, key listening and also has a few functions. Other than that seems kinda self-explaining. they do nothing other than some int algorithms and drawing in the canvas.
-- Figured out --
I understood that I should calculate time spent for current function and not since the last one started. My old method worked not in 2 channels but rather in long-short-long-short-long-... delayes

first of all you dont set the running bool and also when you enter the function immediately do a on clearTimeout on drawTimer.
clearTimeout(drawTimer);

In a loop like that, you should consider to write:
if(timeElapsed >= AMOUNT_OF_TIME)
{
// run code
}

Related

phaser-framework javascript "infinite loop" "browser crash" timer

I'm calling this a browser crash, but more like an infinite loop - I'm hardly a javascript expert. What happens is a game gets to it's end, and the browser (in this case firefox) just goes on and on and on - more like a freeze, no snap, error message, no nothing. You have to close down the browser completely. Now the really odd thing about this is this works perfectly fine - off line. This just happens online. And the other odd thing about this is that (in the code) the first time it resets the game - this also works perfect both online and off. Just the second time.
Like this: (this is part of it)
function resetGame(){
//reset game
Ok the above works online and off, player gets dumped out of game, starts over. Then however;
function moveToEndGame(){
console.log("TIMER END");
var twn = game.add.tween(bg).to({alpha:0},tween_speed,"Linear",true);
twn.onComplete.addOnce(function(){flagGameover = true;},this);
}
if(!flagGameover && !star.visible && idx_bullet < bullet_array.length)
initBullet();
else{
if(flagGameover){
console.log("GOTO GAMEOVER");
window.location = "../endgame.html";
}
}
}
}
OK, offline it goes directly to endgame.html online, it freezes up, crashes, infinitive loop, whatever you want to call it.
Anyone have any idea why?
OK, spent hours and hours on this (the programmer who did this part has since disappeared). Even went back 2 weeks ago to the code as it was, yup, that does the same thing - infinite loop.
So now I'd like to find a way to just 'abort the javascript' right at the end of the 21 second timer. I've run across a number of things from other posts such as:
// creates a new exception type:
function FatalError(){ Error.apply(this, arguments); this.name = "FatalError"; }
FatalError.prototype = Object.create(Error.prototype);
or
function javascript_abort()
But....
Even if you throw an exception, it will only kill the current event loop. Callbacks passed to setTimeout or DOM/XMLHttpRequest event handlers will still run when their time comes.
or something with this:
functions (setTimer and two setIntervals)
OR
function clearTimer() {
clearInterval(intervalTimer);
}
Anyway, here are the sections in the code pertaining to the 21 second timer:
var maxTime = 21; //in SECOND
var timerEvent = null;
This is where the timer gets added (after a 3 second animation)
console.log("FINISH ANIMATION and ADD TIMER");
and this
timerEvent = game.time.events.add(Phaser.Timer.SECOND * maxTime, moveToEndGame, this);
},this);
function moveToEndGame(){
console.log("TIMER END");
var twn = game.add.tween(bg).to({alpha:0},tween_speed,"Linear",true);
twn.onComplete.addOnce(function(){flagGameover = true;},this);
}
-----------> and THE END (or supposed to be the end)
if(!flagGameover && !star.visible && idx_bullet < bullet_array.length)
initBullet();
else{
if(flagGameover){
console.log("GOTO GAMEOVER");
window.location = "../endgame.html";
}
}
}
}
So is it possible to kill the timer at the end of 21 seconds and everything else with it and just have the player go to endgame.html ????????? i didn't mention the phaser console.log - can it be done that way? Anyone do something like this before, basically a javascript salvage operation.
I found out what it was.
It was missing a callover, as in:
var callGameover = false;
and......
if(flagGameover){
if(callGameover == false){
callGameover = true;
console.log("GOTO GAMEOVER");
window.location = "endgame.html";
I didn't know that was enough to cause an "infinite loop" but apparently it was. Put the above in and loop gone!

CesiumJS - How can I control viewer time and ticks?

What I'd like to do is control the clock ticks for a non-realtime Cesium application. Imagine there's expensive code running, plus I want to give the viewer time to load the tiles before continuing. So how do I disable automatic ticking, and then call tick() manually when my code is ready for it?
Docs for Cesium.Clock say "The clock will only tick when both Clock#canAnimate and Clock#shouldAnimate are true." but that's not what I'm getting.
What I currently see:
viewer.clock.canAnimate = false;
viewer.clock.shouldAnimate = false;
viewer.clock.onTick.addEventListener(function(clock){
console.log("Tick");
});
The result in the console shows the clock still ticking:
Tick
Tick
Tick
Tick
...
What I'd like to do:
viewer.clock.stopTicking(); // or whatever that command would be...
while (someCondition){
// run expensive code
tick(); // issue manual tick
}
Thanks for your help!
Max
It's a bit of a legacy quirk of the Cesium API that the Clock's onTick event fires for every animation frame rendered, regardless of whether the clock advances in time or not.
If you want to take control of Cesium's render loop yourself, you can do that like this:
viewer.useDefaultRenderLoop = false;
function myOwnRenderLoop() {
viewer.resize();
viewer.render();
Cesium.requestAnimationFrame(myOwnRenderLoop);
}
Cesium.requestAnimationFrame(myOwnRenderLoop);
Above, I'm using requestAnimationFrame, so the loop runs as fast as possible. But I could replace that with setTimeout to get a slower loop, emulating poor render performance. Note that interactivity and screen updates would slow down with such a method when longer time intervals are used.
viewer.useDefaultRenderLoop = false;
function myOwnRenderLoop() {
viewer.resize();
viewer.render();
window.setTimeout(myOwnRenderLoop, 500);
}
window.setTimeout(myOwnRenderLoop, 500);
So, your console.log is still printing 'Tick' because the onTick continues to fire, regardless of whether the clock is advancing. All you need to do is toggle both the canAnimate and shouldAnimate, as you suspected. So, your example code would basically be:
viewer.clock.canAnimate = false;
viewer.clock.shouldAnimate = false;
while (someCondition){
// run expensive code
// toggle someCondition so we can exit this
}
// set the animate bools back to true so the clock can advance
viewer.clock.canAnimate = true;
viewer.clock.shouldAnimate = true;
To better see this in action, try this (and maybe set the if conditional to 1000 instead of 100):
viewer.clock.canAnimate = false;
viewer.clock.shouldAnimate = false;
var s = 0;
viewer.clock.onTick.addEventListener(function(clock){
if (s < 100) {
console.log(viewer.clock.currentTime);
} else {
viewer.clock.canAnimate = true;
viewer.clock.shouldAnimate = true;
}
s++;
});
You'll see that the console.log is printing the same value for 100 (or 1000) times...this is because the currentTime isn't advancing because of the canAnimate and shouldAnimate. Once those are both toggled back to true, the currentTime will advance.

Call function when <audio> is at specific time

I am currently doing some fun website, which requires audio cues (I assume that's the name?). I want the site to do something, when the song has been played for exactly X amount of time.
I can easily get the current time using element.currentTime, but I have no clue how to say: when element.currentTime == 5.2, runFunction() - If you know what I mean. Is there some kind of way this could be done? My current test code:
<----AUDIO WILL START PLAYING---->
http://jsfiddle.net/jfL4mcnh/
$("<audio id='audioElement'>").appendTo("body");
$("#audioElement").attr("src", "http://mp3ornot.com/songs/1B.mp3").attr("autoplay", "autoplay");
setInterval(function() {
//for some reason, $("#audioElement").currentTime won't work, so we're going old fashion
time = document.getElementById("audioElement").currentTime;
console.log(time);
}, 1000);
Also, I forgot to say this, I cannot do a setTimeout() and hit at the exact moment I want in milliseconds, because the audio can take some extra time to load, while the actual code runs exactly when it has been "seen", if you know what I mean. So no countdown. I need to be exact here.
If you need greater resolution than ontimeupdate provides, you can use a setInterval instead.
Live Demo (sound and alert box only!):
$("<audio id='audioElement'>").appendTo("body");
$("#audioElement").attr("src", "http://mp3ornot.com/songs/1B.mp3").attr("autoplay", "autoplay");
var triggered = false;
var ael = document.getElementById("audioElement");
var interval = setInterval(function(){
console.log(ael.currentTime);
if (!triggered && ael.currentTime >= 5.2) {
triggered = true;
alert("5.2 seconds reached");
}
if (ael.ended) clearInterval(interval);
}, 50);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
JSFiddle Version: http://jsfiddle.net/jfL4mcnh/15/
Well, I have done it myself.. It seems.
http://jsfiddle.net/jfL4mcnh/13/
$("#audioElement").bind("timeupdate", function() {
var currentTime = parseInt(this.currentTime, 10);
if(currentTime == 2) {
console.log("2 seconds in");
$(this).unbind("timeupdate");
}
});
You can bind timeupdate to it, then unbind it (apparently it runs the code 4 times, so I have to unbind it).
EDIT: Nope, it doesn't update fast enough to make it perfect on point. It increments each ~300ms it seems.
See this jsfiddle here
I've added the following line to the JavaScript setInterval() function:
if (time > 5.2) {
myFunction();
}
myFunction() does a console.log, which you'll see in the console.
The reason I used > rather than === is that the time reported is never precise due to fluctuations in processing. A Boolean in the condition would solve this problem:
triggered = false;
if (time > 5.2 && !triggered) {
triggered = true;
myFunction();
}

setInterval works then hangs browser javascript

I'm wanting to animate an element using setInterval. I've put my code into an object with 2 functions one to initialize and one to animate using setInterval. When I try to run the code the animation works once then causes the browser to hang. The only thing I can think of is an infinite loop being created somewhere however I can't see anywhere in my code that would cause this.
What is causing the browser to crash, how can this be overcome ?
<div id='box' style='position:absolute;height:100px;width:100px;background-color:#44e'>box</div>
<script>
var box = {
init : function(elemId) {
box.elem = document.getElementById(elemId);
box.timer = setInterval;
box.tick = 0;
box.animate();
},
animate: function() {
if(box.tick < 100) {
box.elem.style.top = box.tick +'px';
box.elem.style.left = box.tick +'px';
box.tick++;
} else {
clearInterval(box.timer);
}
var timer = setInterval(box.animate, 50)
}
}
box.init('box');
</script>
setInterval sets up a function that will be called repeatedly by the browser until you cancel the interval timer. Your code isn't doing that, because the only call to clearInterval is using box.timer, which is never set to a timer handle (the return value from setInterval). So you end up scheduling thousands of calls (a new series every time animate is called) and bringing the browser to its kneeds.
At the very least, this:
var timer = setInterval(box.animate, 50)
should probably be:
box.timer = setInterval(box.animate, 50);
Or you may want setTimeout (which schedules only one call back).

Javascript threading race condition

EDIT: I figured out the answer to the original YUI3 question I posted here, but it led to another one and instead of starting a new thread I thought I'd just add it here. Please scroll down for the new question (it's bolded).
Original question:
I'm having some issues creating a JavaScript countdown timer inside a YUI definition, my guess is something to do with object scoping. Here's my code:
YUI({combine: true, timeout: 10000}).use("node", function (Y) {
var timer = new function(){};
Y.augment(timer, Y.EventTarget);
timer.on('timer:down', function() {
Y.log('timer down event fired', 'event');
Y.Lang.later(1000, Y, timer_trigger());
});
timer.on('timer:end', function() {
Y.log('timer end event fired', 'event');
});
var timer_from;
function startTimer(seconds){ // start a coundown from seconds to 0
timer_from = seconds;
timer_trigger();
}
function timer_display(){
var mins = Math.floor(timer_from/60);
var secs = timer_from - mins*60;
var secsDisp = secs;
if(secs<10){
secsDisp = '0' + secs;
}
Y.one('#timer').set('innerHTML', mins + ':' + secsDisp);
}
function timer_trigger(){
Y.log('timer from is: '+timer_from);
if(timer_from > 0){
timer_from--;
timer_display();
if(timer_from > 0){
timer.fire('timer:down');
}
} else {
timer.fire('timer:end');
}
}
function initializePage(){
startTimer(900);
}
});
The error I'm getting is that it doesn't wait the 1000ms like I'm asking it to to call timer_trigger() and Safari eventually asks me whether I want to stop running the code. When I do a few seconds after loading the page, the timer is already down to about 3, 4 minutes.
I've also tried using setTimeout but that also produces the same result. Can anyone help? I would really appreciate it!
EDIT:
I actually figured out a solution - this came after hours of trying tons of things, but a few more Google searches can sometimes still produce new results/answers (I found the answer on this site, actually).
So apparently my code was creating a race condition, and all I had to do to fix it is this:
setTimeout(function(){
timer_trigger();
}, 1000);
I looked up race conditions, but it's unclear to me what it means in my case, and how the seemingly trivial change to my code fixed the issue I was having. So the original question in answered, but I'd like to turn this into the question that arose from the answer.
How does threading in JavaScript work and what cause my race condition, and why did the minor change in code fix the error I had?
The problem is not a race condition. The reason the additional call to setTimeout "fixes" your code is because of a logic flaw in timer_trigger. Consider what happens in the case where timer_from is 1 when the function is called. Neither timer:down nor timer:end will be triggered.
function timer_trigger(){
Y.log('timer from is: '+timer_from);
if(timer_from > 0){ // Since timer_from is 1, the if block is entered
timer_from--; // timer_from is 0
timer_display();
if(timer_from > 0){ // This block is not entered, but it has no matching else
timer.fire('timer:down');
}
} else { // The matching if block was entered, so this is not
timer.fire('timer:end');
}
}
You added this code:
setTimeout(function(){
timer_trigger();
}, 1000);
This causes timer_trigger to be called once more with timer_from already set to 0, allowing the else block to be executed.
Also note that
Y.Lang.later(1000, Y, timer_trigger());
executes timer_trigger immediately and passes the return value to Y.Lang.later. You probably meant
Y.Lang.later(1000, Y, timer_trigger);

Categories