What this is all about
I am currently working on a script for emoticons on the Campfire Chat Service since they don't have an emoticon feature. Right now it is running the emoticon really slowly since it uses a setInterval that is suppose to check ever 200 milliseconds if there is a new message in the chat. I do this by making two variables "a" and "b" with var a,b;. So then in the first setInterval I time it with 150 millisecond. Inside of this I just give the variable "a" the value of the HTML of the chat. Then in the second setInterval (200) I give the variable "b" the value of the HTML too but this is a different timing so now I check the HTML in an if statement and if it is different I run the function to change the Symbol to an emoticon. I did it this was to improve performance. Before it would check for emoticons even though no one in the Campfire Chat actually wrote something so there is no need to check for emoticons.
This is the complete function:
var a, b;
setInterval(function() {
a = $('#chat').html();
console.warn("Chat HTML has been Scanned");
}, 150);
var t = "0";
setInterval(function() {
b = $('#chat').html();
console.warn("Chat got scanned for Change in html.");
if (emoOn == true) {
if (t === $('#num12345').text()) {
$(".participants").after('<div id="emote"><h3><span class="room_actions">On | Off</span>Emoticons</h3></div>');
$('#num12345').text("111")
}
if (a !== b) {
turnOn();
console.log("CHANGE")
} else {
turnOn("off");
}
}
}, 200);
The problem
As you can see in the above code I run 3 consoles. In the consoles I expect the "Chat HTML has been Scanned" warning to come for and then the "Chat got scanned for Change in html." in that order for the whole time. But instead the "Chat HTML has been Scanned" warning shoes up twice it just doesn't work like I image it to work.
And when a new message shows its suppose to log "Change" but most of the time it doesn't and I think it is the problem of the setIntervals. It seems like they aren't timing right. But I really don't wanna say that JavaScript is causing this issue but I don't know what else it could be. Also I'm using Chrome.
setInterval is asynchronous, so when your first call is waiting, the javascript interpreter moves on to it's next job (in this case setting t to "0" and then your second setInterval call).
Edit: I think the problem is that you're defining your setInterval calls at the same time, yet the precision of javascript timers is not that great (see here: http://ejohn.org/blog/accuracy-of-javascript-time/).
You could call the second setInterval after a setTimeout to give yourself a bit of a buffer:
var a, b;
setInterval(function() {
a = $('#chat').html();
console.warn("Chat HTML has been Scanned");
var t = "0";
}, 2000);
setTimeout(function(){
setInterval(function() {
b = $('#chat').html();
console.warn("Chat got scanned for Change in html.");
if (emoOn == true) {
if (t === $('#num12345').text()) {
$(".participants").after('<div id="emote"><h3><span class="room_actions">On | Off</span>Emoticons</h3></div>');
$('#num12345').text("111")
}
if (a !== b) {
turnOn();
console.log("CHANGE")
} else {
turnOn("off");
}
}
}, 2000);
}, 500);
This will set up the second call to setInterval half a second after the first, you could get away with a lot less than that though (anything over 30ms should be enough).
Related
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've searched for how to use setTimeOut with for loops, but there isn't a lot on how to use it with while loops, and I don't see why there should be much difference anyway. I've written a few variations of the following code, but this loop seems to crash the browser:
while(src == '')
{
(function(){
setTimeout(function(){
src = $('#currentImage').val();
$("#img_"+imgIdx).attr('src',src);
}, 500);
});
}
Why?
Basically I have an image created dynamically whose source attribute takes time to load at times, so before I can display it, I need to keep checking whether it's loaded or not, and only when its path is available in $('#currentImage'), then do I display it.
This code worked fine before I used a while loop, and when I directly did
setTimeout(function(){
src = $('#currentImage').val();
$("#img_"+imgIdx).attr('src',src);
}, 3000);
But I don't want to have to make the user wait 3 seconds if the loading might be done faster, hence I put the setTimeOut in a while loop and shorted its interval, so that I only check for the loaded path every half second. What's wrong with that?
The while loop is creating trouble, like jrdn is pointing out. Perhaps you can combine both the setInterval and setTimeout and once the src is filled, clear the interval. I placed some sample code here to help, but am not sure if it completely fits your goal:
var src = '';
var intervalId = window.setInterval(
function () {
if (src == '') {
setTimeout(function () {
//src = $('#currentImage').val();
//$("#img_" + imgIdx).attr('src', src);
src = 'filled';
console.log('Changing source...');
clearInterval(intervalId);
}, 500);
}
console.log('on interval...');
}, 100);
console.log('stopped checking.');
Hope this helps.
The problem is probably that you're not checking every half second.
setTimeout schedules a function to run at a future time, but it doesn't block, it just runs later. So, in your while loop you're scheduling those functions to run just as fast as it can iterate through the while loop, so you're probably creating tons of them.
If you actually want to check every half second, use setInterval without a loop instead.
Thanks everyone - all the suggestions helped. In the end I used setInterval as follows:
var timer;
// code generating dynamic image index and doing ajax, etc
var checker = function() {
var src = $('#currentImage').val();
if(src !== '') {
$('#img_' + imgIdx).attr('src', src);
clearInterval(timer);
}
};
timer = setInterval(checker, 500);
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
}
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);
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.