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

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!

Related

JavaScript not finishing DOM update and moving to next function

EDIT: While this question has been answered and very well, I wanted to share another answer I found that I think explains the issue more in depth just for the sake of learning:
Javascript - Are DOM redraw methods synchronous?
Rock Paper Scissors Github Project
The issue:
I am writing a program Rock Paper Scissors for The Odin Project. This calls a function updateElements() to change the textContent of on page variables to display the round statistics. This functions works properly until the win condition is met in the function roundCounter(); however, roundCounter() should not be called before the function updateElements() completes.
updateElements() -> roundCounter() -> globalReset()
A win or loss condition is met when any of the round counting variables reaches 3. This can result in a win, draw, or loss. In any condition being met a prompt occurs which asks the player to either begin a new game or not. This is my error, it seems that the win condition is somehow met prior to updateElements() being able to finish updating the DOM.
Console logs left from when it was console only, show that the variables are on the correct iteration.
One possible solution that my mentor and I worked on was causing a timeOut to occur prior to roundCounter() being called. This does work if set to anything greater than 20ms. Yes this is a solution. I am here asking for more understanding on what and why this issue is happening. Why does the DOM not draw the new values before the next function? Why does the prompt stop the DOM from updating? Any help would be much appreciated!
function updateElements() {
let pScore = `${playerWins}`;
let cScore = `${computerWins}`;
let dCount = `${nobodyWins}`;
let rCount = `${roundCount}`;
PLAYER_SCORE.textContent = pScore
COMPUTER_SCORE.textContent = cScore;
DRAW_COUNT.textContent = dCount;
ROUND_COUNT.textContent = rCount;
roundCounter();
}
Which then moves to roundCounter()
function roundCounter() {
console.log(`Your wins: ${playerWins} \nComputer Wins: ${computerWins} \nDraws: ${nobodyWins} \nRound Count: ${roundCount}`);
if (roundCount === 5 && computerWins < 3 && playerWins < 3 && nobodyWins < 3 ) {
console.log("This game was a draw");
newGamePrompt();
} else if (roundCount <= 5) {
if (playerWins === 3) {
console.log("You are the winner!");
newGamePrompt();
} else if (computerWins === 3) {
console.log("You lose!");
newGamePrompt();
} else if (nobodyWins === 3) {
console.log("Nobody won!");
newGamePrompt();
}
} else if (roundCount > 5) {
console.log(
"An error has occured: The game has run too many rounds. Restarting");
newGameYes();
}
}
Prompt displays before DOM updates
Canceling the Prompt causes DOM to finish updating
Troubleshooting Steps taken:
Removing the newGamePrompt(), setting playAgain locally to "no"
no change.
Debugger:
Counter updates appropriately.
Executes in appropriate order.
console logging:
updateElements() and roundCounter() show the correct value.
There are 2 things to know here:
When you use prompt (or its siblings alert and confirm), everything on the page stops until the box is dismissed. (other JS doesn't run, and the browser can't repaint.)
The browser waits to repaint the screen until the main thread's JavaScript execution is finished.
If you change the DOM, then immediately call prompt, the DOM update will not be seen until after the prompt finishes and the thread ends.
Here's a simpler example of this problem in action:
document.body.innerHTML += 'you will not see this until dismissing the prompt';
prompt('foo');
Doing something like putting further logic inside a small setTimeout is a very common solution to this problem (but more elegant would be
requestPostAnimationFrame once it gets supported, or with its polyfill, thanks Kaiido).
An even better solution would be to avoid prompt and its siblings entirely, since they block the page - they're so user-unfriendly and can cause unintuitive behavior like this. Create a proper modal instead. It'll take a bit more code, but it's a much more elegant solution.

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.

Automating scrolling at randomly timed intervals in JS based on events

I'm writing a Chrome extension that scrolls & listens for newly added child nodes to a parent node.
It then waits a random amount of time, then scrolls down again if more children are added, and stops if in the next 5 seconds nothing appears via ajax (when the list of results has been exhausted, for example).
My question is how I should handle waiting variable amounts of time between each event scroll reaction.
I'd like for it to work politely (yet not fail to scroll down if all 50 elements are loaded at once and the scrolls generated aren't quite enough to get to the next ajax load point).
Any ideas or ways I should think about this? (This is for a totally benign use case btw)
A good solution is a little tricky, but totally works:
var canPoll = true;
var timeout = ... whatever ...; // if we want an absolute timeout
var startTime = (new Date()).getTime();
function randomWait(t) {
// ... waits a random period of time
}
function isDone() {
// ... check if done. returns boolean
}
function complete() {
// ... run when everything is done
}
(function recursive() {
// check for our absolute walltime timeout
canPoll = ((new Date).getTime() - startTime) <= timeout;
// check other conditions too
if (!fn() && canPoll) {
// repeat!
setTimeout(recursive, randomWait(interval));
} else {
// we're done
complete();
}
})();
Adapted from this wonderful answer.

JavaScript setTimeout Runs Twice

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
}

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