The task I want my program to complete successfully is for it to intake a value from the command prompt variable ask and check if it was correct. But if the player doesn't answer in time, the set timeout function would execute therefore subtracting a life.
My problem is when I enter the prompt ask the timeout function isn't setting off at all.
Also, I have the whole thing in a for-loop so when the player does get the answer correct, it runs again and subtracts the time so there is less and less time to solve the problem. My code is below.
var win = null;
var product = "123456";
time = 3000;
for(var i = 0; i < 1; i++) {
confirm("Round will start after you close this confirm window.");
setTimeout(function() {
var ask = prompt("Enter code below: (QUICK) " + product + " (Answer for development purposes)");
if(ask === product){
win = true;
console.log("Hurrah!");
} else {
win = false;
console.log("Arr. Well get em next time.");
}
}, 1);
//Why is this not activating???
setTimeout(function() {
if (win === null || false) {
lives = lives - 1;
win = false;
console.log("Oh No! You ran out of time. Lives left: " + lives);
}
}, time);
time = time - 100;
}
prompt() is a blocking function that does not return until the user supplies the required input. Because it is blocking, no other Javascript runs while it is waiting for the user input. Thus, your setTimeout() cannot run while prompt() is waiting for input.
So, because it's blocking and it doesn't have a built-in timeout capability, you can't use prompt() to do what you're trying to do. It simply doesn't have that capability and the blocking nature of that function prevents you from using a setTimeout() to make the time out feature work.
Assuming this is in a web page, you could use a different form of non-blocking prompt (such as form elements on a web page). In that case, you could both present the input elements asking for user input and use a setTimeout() that would fire if no input was provided. Because the user interface in that case was non-blocking, other Javascript could run while this UI was presented to the user and you could implement your timeout that way. You would also have to change your code to work off event handlers instead of a blocking prompt() function.
It seems the if statement in your second timeout will never be executed. you can try this
if(win == null || win == false){
lives = lives - 1;
win = false;
console.log("Oh No! You ran out of time. Lives left: " + lives);
}
Related
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.
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!
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
}
There has to be an easy way to do this, but I'm new to JS.
I have a javascript program that (1) takes user input, (2) updates the webpage based on that input, then (3) performs a lengthy calculation. The trouble is that the webpage doesn't register the update till after the lengthy calculation. Isn't there a way to pause execution so that the page can update before the long calculation?
I've tried setTimeout and window.setTimeout, but they made no difference.
The program is for playing a game: the user inputs a move, the script updates the position, then calculates its next move. postMessage prints text messages using div.innerHTML; buttonFn takes the input from the user, updates the position, prints a message, then starts the computer calculating.
function buttonFn(arg){
var hst = histButt;
hst.push(arg);
var nwmv = hst.clone();
postMessage("New move: " + nwmv.join());
if(status == opposite(comp) && !pauseQ){
var mvsposs = movesFromPos(posCur,status);
if(mvsposs.has(nwmv)){
updatePosCur(nwmv);
//waitasec();
if(comp == status && !pauseQ){
compTurn();
};
}
else{
histButt = nwmv;
};
};
};
yes there is, call your function like this. Using setTimeout will allow a page reflow prior to your JS executing.
function buttonFn(arg){
var hst = histButt;
hst.push(arg);
var nwmv = hst.clone();
postMessage("New move: " + nwmv.join());
if(status == opposite(comp) && !pauseQ){
var mvsposs = movesFromPos(posCur,status);
if(mvsposs.has(nwmv)){
updatePosCur(nwmv);
//waitasec();
if(comp == status && !pauseQ){
setTimeout(function(){
compTurn();
},0);
};
}
else{
histButt = nwmv;
};
};
};
Remember, JS is very event driven friendly. If you can move things off, and call them later do it. Thats the only way we can support multi-threaded like behavior.
setTimeout
If you only need to support modern browsers (or if you use a transpiler), you can now use ES6 features to make this much easier and more in the style the original questioner was trying to do. (I realize the question is 8 years old - no harm in a new, more current answer!)
For example you can do something like this:
// helper function to use a setTimeout as a promise.
function allowUpdate() {
return new Promise((f) => {
setTimeout(f, 0);
});
}
// An infinitely looping operation that doesn't crash the browser.
async function neverStopUpdating(someElement) {
let i = 0;
while (true) {
someElement.innerText = i;
i++;
await allowUpdate();
}
}
If you're trying to do a hard computation you'll want to make sure not to do this await too frequently - in this example, in Chrome at time of writing, i only increments by about 150 per second because the context switch of a setTimeout is not fast (where you'd get hundreds of thousands in a second if you didn't yield for updates). You'd likely want to find a balance, either always perform some number of iterations before allowing an update, or maybe eg. call Date.now() in your loop and yield for an update whenever 100ms have passed since the last time you allowed an update.
You can do the update, wait for a bit of time, than do the calculation.
OR
You can use webworkers on browsers that support them.
Without having actual code, that is the best answer that I can give you.
JavaScript is single threaded. If you do your calc server side you could get the results via ajax which is called asynchronously, not blocking your ui.
So I made some timers for a quiz. The thing is, I just realized when I put
javascript: alert("blah");
in the address, the popup alert box pauses my timer. Which is very unwanted in a quiz.
I don't think there is any way to stop this behaviour... but I'll ask anyway.
If there is not, mind suggesting what should I do?
Never, ever rely on javascript (or any other client-side time) to calculate elapsed times for operations done between postbacks, or different pages.
If you always compare server dates, it will be hard for people to cheat:
first page request, store the server time
ping with javascript calls each N seconds, compare the 2 server times, and return the elapsed (just for show)
when the user submits the form, compare the 2 server times, calculate the elapsed time, and discard the ones which took too long (ie: possible cheaters)
Apparently the preview rendering differs from the posted rendering. This paragraph is here to make sure the next two lines show up as code.
// Preserve native alert() if you need it for something special
window.nativeAlert = window.alert;
window.alert = function(msg) {
// Do something with msg here. I always write mine to console.log,
// but then I have rarely found a use for a real modal dialog,
// and most can be handled by the browser (like window.onbeforeunload).
};
No, there is no way to prevent alert from stopping the single thread in JavaScript. Probably you can use some other way of user notification, for example a floating layer.
It's modal and stops execution. Consider an alternative which does not pause execution like a Lightbox technique.
I think the question asker is trying to prevent cheating. Since a user can type javascript: alert("paused"); into the address bar, or make a bookmarklet to do that, it's easy to pause the quiz and cheat.
The only thing I can think of is to use Date() to get the current time, and check it again when the timer fires. Then if the time difference is not reasonably close to the intended timer duration, show an admonishment and disqualify the answer to that question or let them flunk the quiz. There is no way to prevent the user from pausing your quiz, but it should be possible to catch them.
Of course with any cheat-proofing, you motivate people to become better cheaters. A person could change the system time on their PC, and fool the javascript Date() constructor which gets the time from the operating system.
You can use an interval to do a repeated clock comparison against a one second interval length. The interval handler can also update a time-remaining field on the user's display. Then the users can feel the pressure build as time runs out on their quiz. Fun times!
The feedback loop on SyaZ's question has clarified the issues at stake.
Here's an attempt to summarize the good answers so far:
Client scripts are by nature are easy to manipulate to cheat an online quiz. SEE #Filini 's Server-side approach
window.alert = function(msg) {} will overriding alert() and perhaps defeat the low hanging fruit of putting in the addressbar: javascript:alert('Pausing page so I can google the answer') or I'll use my Phone-A-Friend now. Courtesy of #eyelidlessness
If you must use a client-side approach, instead of using setTimeOut(), you could use a custom date-compare-based pause function like this (concept by #Mnebuerquo, code example by me (#micahwittman)):
Example:
var beginDate = new Date();
function myTimeout(milsecs){
do { curDate = new Date(); }
while((curDate-beginDate) < milsecs);
}
function putDownYourPencils(milsecs){
myTimeout(milsecs);
var seconds = milsecs / 1000;
alert('Your ' + seconds + ' seconds are up. Quiz is over.');
}
putDownYourPencils(3000);
Ultimately, you cannot trust user input. Without keeping track of the time elapsed on the server, there's just no guarantee the data hasn't been manipulated.
However, if you're confident your quiz-takers aren't JavaScript-savvy, and are merely relying on a "trick" they found somewhere, you could test for cheating (pausing) with the following code, which doesn't require modifying window.alert:
var timer = {
startDatetime: null,
startSec: 0,
variance: 1,
exitOnPause: true,
count: function (config) {
var that = this;
if (typeof config == "object" && typeof parseInt(config.seconds) == "number" && !isNaN(parseInt(config.seconds)))
{
if (typeof parseFloat(config.variance) == "number" && !isNaN(parseFloat(config.variance))) this.variance = config.variance;
if (typeof config.exitOnPause == "boolean") this.exitOnPause = config.exitOnPause;
if (config.seconds > 0)
{
if (!this.startSec) this.startSec = config.seconds;
if (!this.startDatetime) this.startDatetime = new Date();
var currentDatetime = new Date();
if (currentDatetime.getTime() - this.startDatetime.getTime() > (this.startSec - config.seconds) * this.variance * 1000)
{
if (typeof config.onPause == "function") config.onPause();
if (!this.exitOnPause)
{
this.startDatetime = new Date();
this.startSec = config.seconds--;
window.setTimeout(function () { that.count(config); }, 1000);
}
}
else
{
config.seconds--;
window.setTimeout(function () { that.count(config); }, 1000);
}
}
else
{
if (typeof config.onFinish == "function") config.onFinish();
}
}
}
};
This timer object has a single method, count(), which accepts an object as input. It expects a seconds property in the input object at minimum.
For some reason, window.setTimeout doesn't always work as expected. Sometimes, on my machine, window.setTimeout(x, 1000), which should execute the code after 1 second, took more than 2 seconds. So, in a case like this, you should allow a variance, so people who aren't cheating don't get flagged as cheaters. The variance defaults to 1, but it can be overridden in the input object. Here's an example of how to use this code, which allows 2.5 seconds of "wiggle room" for slow-pokes:
timer.count({
seconds: 10,
onPause: function () { alert("You cheated!"); window.location.replace("cheatersAreBad.html"); },
onFinish: function () { alert("Time's up!"); },
variance: 2.5
});
With a solution like this, you could use Ajax to tell a server-side script that the user has paused the timer or redirect the user to a page explaining they were caught cheating, for example. If, for some reason, you wanted to allow the user to continue taking the quiz after they've been caught cheating, you could set exitOnPause to false:
timer.count({
seconds: 10,
exitOnPause: false,
onPause: function () { recordCheaterViaAjax(); },
onFinish: function () { alert("Time's up!"); },
variance: 2.5
});
The server session could be set to expire at say 1 hour. The javascript could be used as only a display tool for the user to know how much time is left. If he decides to cheat by pausing the timer, then he might be suprised when posting his test that his session has timed out.