setTimeout not changing external variable [duplicate] - javascript

This is my code:
var b;
while(!b){
setTimeout(function(){
alert('sss')
b=1;
}, 500);
}
and it will not alert 'sss'
What can i do?
Updated:
I want to get bounds on google maps v3:
function get_bounds(){
var bounds_;
while(!bounds_){
setTimeout(function(){
bounds_=map.getBounds();
if(bounds_){
var leftBottom=[bounds_.getSouthWest().lat(),bounds_.getSouthWest().lng()]
var rightTop=[bounds_.getNorthEast().lat(),bounds_.getNorthEast().lng()]
return [leftBottom,rightTop];
}
}, 500);
}
}
updated2:
hi patrick dw, i don't know why , but your code doesn't work:
var b;
function waitForB() {
setTimeout(function(){
if(!b)
waitForB();
else
alert('sss');
}, 500);
}
waitForB()
updated3:
it is ok now :
var b;
function waitForB() {
setTimeout(function(){
if(!b){
waitForB();
b='ss';
}
else{
alert('sss')
}
}, 500);
}
waitForB()

JavaScript in web browsers is run in a single thread. When you call setTimeout(), it won't spawn a new thread. This means that setTimeout() will not execute until all of your main code has finished executing.
For this reason, you will end up with an infinite loop, because your loop condition is dependant on the execution of the setTimeout() callback.
Here's an interesting article on how JavaScript timers work:
How JavaScript Timers Work by John Resig
UPDATE:
Further to the updated question, you may want to listen to the bounds_changed event instead. I am not sure how you are planning to use your get_bounds() function, but you may want to refactor your logic to use an event listener instead:
google.maps.event.addListener(map,'bounds_changed', function () {
// The code here is triggered when the bounds change
});

That code is going to burn CPU time and memory by scheduling timeouts to happen. Think about it: you're loop condition is "b" becoming truthy. How is that going to happen? Only when a timer event fires. Will that happen? No, because you're eating the whole machine scheduling zillions more timeouts.
This sort of situation has as a tell-tale sign the effect of warming up the room you're sitting in.
I don't know what effect you're trying to get. Why not start by just the setTimeout() call and see how that goes. Maybe you could describe more about what it is you're trying to do.

Maybe you will want to use setInterval instead of setTimeout.
When b is changed, alert shows up.
var b = false;
(function () {
var intervalId;
function wait() {
if (b) {
clearInterval(intervalId);
alert('sss');
}
}
intervalId = setInterval(wait, 500);
})();
It is more intuitive and it doesn't mess with global variables too much.
HINT: Put semicolon after every statement if you are not sure where to omit safely.

This problem can now be solved correctly using the idle rather than the bounds_changed event listener:
google.maps.event.addListener(map, 'idle', function() {
updateStuff();
});
This event is fired when the map becomes idle after panning or zooming.
http://code.google.com/apis/maps/documentation/javascript/reference.html
It also fires after the map is first rendered, so this is probably the only event listener you need on your map in order to keep it up to date.

Related

stopping a nested javascript function running more than once

I'm trying to get a javascript function to run only once. I've seen this question has been asked before, e.g. Function in javascript that can be called only once, but I can't get the solutions in here to work. I'm not sure if it's because I've got nested functions, or whether there's something I'm missing. Essentially, I'm trying to run a function which, when a webpage is scrolled, it:
- runs a little animation on a canvas in the header
- reduces the size of the header
- leaves it at that
But when there is any subsequent scrolling, the animation keeps re-running. Here's a summarised version of the non-working code:
$(document).on("scroll",function(){
var arrange_title = function(){
//some code
};
if($(document).scrollTop()>0){
arrange_title();
arrange_title = function(){};
setTimeout(function(){
$("header").removeClass("large").addClass("small");
},1000);
}
});
I've also tried declaring a global variable, setting it to "false" in a "window.onload" function, then set it to true in an if function that runs the animation (the if function running only if the variable is false), but that doesn't stop it either. Thoughts?
What you're looking for is something along the lines of listenToOnce where the listener fires the one time, but never again. This could be modified to a number of calls, but the logic is like so:
Register the listener.
Then once the listener fires, remove it.
See .off
$(document).on("scroll",function(){
var arrange_title = function(){
//some code
};
if($(document).scrollTop()>0){
arrange_title();
arrange_title = function(){};
setTimeout(function(){
$("header").removeClass("large").addClass("small");
// $(document).off('scroll'); // or here
},1000);
}
$(document).off('scroll'); // remove listener, you can place this in the setTimeout if you wish to make sure that the classes are added/removed
});
Don't use a time out. That is why you are getting in trouble. Declare a variable outside of your function using var, that will make it global. Your code should be inside of a check for that variable. Before executing your code the first time but inside of the check, change that variable so that the code will never run again.
Try avoid setTimeout. Almost all animation can be watched for end.
function doHeaderAnimation() {
return $('header').animate();
}
function makeHeaderSmall() {
$("header").removeClass("large").addClass("small");
}
function handleScroll(event) {
if ($(document).scrollTop() > 0) {
doHeaderAnimation().then(makeHeaderSmall);
$(document).off("scroll", handleScroll);
}
}
$(document).on("scroll", handleScroll);

How to stop the previous instances of the same function if it's called multiple times?

I have written a custom animation function. It usually works just fine, but when I call animate(); in rapid succession with different endCallbacks, sometimes the callbacks overlap really badly, causing the wrong action at the wrong time.
The problem is that the function instantiates multiple times and executes untill the endValue is reached. The currentValue is changed so fast that I get to see just the last value in my html page animation. This hiddes this unwanted behavior.
What I need when I call animate(); a second time is to end the first instance of animate(); and trigger a new one with new values and a new callback. Also at the same time I want to stop the setTimeout() function just to make sure no wrong callback is triggered.
window.onload = function(){
document.addEventListener('click', // some button
function (){
animate(1, 10);
}, false
);
}
function animate(startValue, endValue, callback, endCallback) {
var startValue = startValue,
currentValue = startValue,
endValue = endValue,
callback = callback,
timeout = null;
loopAnimation();
function loopAnimation(){
if (currentValue != endValue){
timeout = setTimeout(function(){
currentValue++;
// Callback executes some page manipulation code
if (typeof callback !== "undefined") callback(currentValue);
console.log(currentValue);
loopAnimation();
},500)
} else {
console.log("This callback triggers some specific changes in my page");
if (typeof endCallback !== "undefined") endCallback();
}
}
}
Instead of seeing in the console:
1,2,3, - 1,4,2,5 ... 6,9,7,10,8,9,10
I'd like to see just:
1,2,3, - 1,2 ... 7,8,9,10
However, keep in mind that because of the way I use animate() in my script I can't relly on knowing the name or scope of the input variables. This cuts me from being able to solve it myself.
While it isn't quite the implementation you're asking for, I wonder if Underscore's throttle or debounce would meet the need?
debounce will make sure your function is called no more than X times per second -- it'll still be executed once per every time called, but the subsequent calls will be delayed to meet your rate limit. So if you called animate twice in quick succession, debounce can delay the second execution until 100ms after the first or what have you.
throttle will basically ignore calls that occur during the rate limit. So if you call your animate 10 times within 100ms, you could have it throw out all but the first. (Actually, it'll do the first one, plus one at at the end of the wait period).
You don't need to use all of underscore to get these methods; I've seen people frequently copy and pasting just the debounce and/or throttle functions from underscore. If you google, you can find some standalone throttle or debounce implementations.
Throttle and debounce are commonly used in just your case, animation.
For your original spec, to actually "end the first instance of animate()" -- there's no great reliable way to do that in javascript. There's no real general purpose way to 'cancel' a function already being executed. If you can make it work with debounce or throttle, I think it will lead to less frustration.
What you need is to store the last timeout id you used. So next time you start a new animation, you clear any ongoing animation using this timeout id and clearTimeout.
I found convenient to store the interval on the function itself.
See the jsbin here :
http://jsbin.com/nadawezete/1/edit?js,console,output
window.onload = function(){
document.addEventListener('click', // some button
function (){
animate(1, 10);
}, false
);
};
function animate(startValue, endValue, callback, endCallback) {
var currentValue = startValue;
if (animate.timeout) clearTimeout(animate.timeout);
loopAnimation();
function loopAnimation(){
if (currentValue != endValue){
animate.timeout = setTimeout(function(){
console.log(currentValue);
currentValue++;
// Callback executes some page manipulation code
if (callback ) callback(currentValue);
loopAnimation();
},500);
} else {
console.log("This callback triggers some specific changes in my page");
if (endCallback) endCallback();
}
}
}

What is the correct way to do an infinite loop that calls a method in Jquery or Javascript?

I have a page that I want to update non stop, every few seconds.
For this, I wrote the following:
var to;
$(function () {
to = setTimeout(updateDivContent, 2000);
});
function updateDivContent() {
$('#topbox').load('/home/blabla', null);
$('#leftgraph').load('/home/blabla', null, function () {
to = setTimeout(updateDivContent, 2000);
});
};
This worked, however, it leads to what I presume is a memory leak as after around 15 minutes, the computer almost freezes up with the browser taking up all available memory and CPU.
I am guessing that the Timeout is basically stacking, but, I am not sure how to fix this. I have tried getting rid of the second timeout and putting the first one inside a while(true) loop, but, I just couldn't get it to work.
Can anyone suggest anything?
This looks fine actually. But if the first Ajax call does not finish within two seconds, it will stack, and this could (don't know for sure) cause problems.
The timeout itself does not stack, since you are initiating a new one only after the previous one finished.
Try to initiate a new timeout once both Ajax requests finished:
$.when($('#topbox').load('/home/blabla'),
$('#leftgraph').load('/home/blabla')
).then(function () {
setTimeout(updateDivContent, 2000);
});
Reference: $.when
I think it is better to use setInterval instead of setTimeOut.
See this post.
You probably want to call clearTimeout to invalidate the previous timer, like this:
clearTimeout(to);
to = setTimeout(updateDivContent, 2000);
can you this it will call ever 2 second
to = setInterval("updateDivContent", 2000);
function updateDivContent() {
$('#topbox').load('/home/blabla', null);
$('#leftgraph').load('/home/blabla', null, function () {
//to = setTimeout(updateDivContent, 2000);
});
};
Try setInterval:
var to;
$(function () {
to = setInterval(updateDivContent, 2000);
});
function updateDivContent() {
$('#topbox').load('/home/blabla', null);
$('#leftgraph').load('/home/blabla')
};

setTimeout in loop to check for a change in bounds

This is my code:
var b;
while(!b){
setTimeout(function(){
alert('sss')
b=1;
}, 500);
}
and it will not alert 'sss'
What can i do?
Updated:
I want to get bounds on google maps v3:
function get_bounds(){
var bounds_;
while(!bounds_){
setTimeout(function(){
bounds_=map.getBounds();
if(bounds_){
var leftBottom=[bounds_.getSouthWest().lat(),bounds_.getSouthWest().lng()]
var rightTop=[bounds_.getNorthEast().lat(),bounds_.getNorthEast().lng()]
return [leftBottom,rightTop];
}
}, 500);
}
}
updated2:
hi patrick dw, i don't know why , but your code doesn't work:
var b;
function waitForB() {
setTimeout(function(){
if(!b)
waitForB();
else
alert('sss');
}, 500);
}
waitForB()
updated3:
it is ok now :
var b;
function waitForB() {
setTimeout(function(){
if(!b){
waitForB();
b='ss';
}
else{
alert('sss')
}
}, 500);
}
waitForB()
JavaScript in web browsers is run in a single thread. When you call setTimeout(), it won't spawn a new thread. This means that setTimeout() will not execute until all of your main code has finished executing.
For this reason, you will end up with an infinite loop, because your loop condition is dependant on the execution of the setTimeout() callback.
Here's an interesting article on how JavaScript timers work:
How JavaScript Timers Work by John Resig
UPDATE:
Further to the updated question, you may want to listen to the bounds_changed event instead. I am not sure how you are planning to use your get_bounds() function, but you may want to refactor your logic to use an event listener instead:
google.maps.event.addListener(map,'bounds_changed', function () {
// The code here is triggered when the bounds change
});
That code is going to burn CPU time and memory by scheduling timeouts to happen. Think about it: you're loop condition is "b" becoming truthy. How is that going to happen? Only when a timer event fires. Will that happen? No, because you're eating the whole machine scheduling zillions more timeouts.
This sort of situation has as a tell-tale sign the effect of warming up the room you're sitting in.
I don't know what effect you're trying to get. Why not start by just the setTimeout() call and see how that goes. Maybe you could describe more about what it is you're trying to do.
Maybe you will want to use setInterval instead of setTimeout.
When b is changed, alert shows up.
var b = false;
(function () {
var intervalId;
function wait() {
if (b) {
clearInterval(intervalId);
alert('sss');
}
}
intervalId = setInterval(wait, 500);
})();
It is more intuitive and it doesn't mess with global variables too much.
HINT: Put semicolon after every statement if you are not sure where to omit safely.
This problem can now be solved correctly using the idle rather than the bounds_changed event listener:
google.maps.event.addListener(map, 'idle', function() {
updateStuff();
});
This event is fired when the map becomes idle after panning or zooming.
http://code.google.com/apis/maps/documentation/javascript/reference.html
It also fires after the map is first rendered, so this is probably the only event listener you need on your map in order to keep it up to date.

jQuery infinite function execution

We want to know if it is possible to have a function using jQuery to inspect a number of elements and, depending on the types assigned to them by one click, perform other functions. Basically, a function that would run forever, while the user does not refresh the page.
The idea is not to depend on events clicks to perform a function, but the classes assigned to a specific element.
For example:
$("td.gantt").each(function() {
if($(this).hasClass("oper")) {
//execute a serie of functions
}
if($(this).hasClass("preop")) {
//execute a serie of functions
}
});
The above is executed once, and we need to run all the time.
// define a function...
function ganttEach() {
$("td.gantt").each(function() {
// ...
});
}
// ...repeat it once every second
window.setInterval(ganttEach, 1000);
You can't "let it run all the time" (like, in a while(true) loop) because JavaScript is single-threaded and blocking the thread means your other code will never run. setInterval() makes sure there are necessary "gaps" for other code to execute.
setInterval() returns an ID that you can store in a variable and feed to clearInterval() at some point to make it stop again.
If you want to make sure that every new iteration of your function starts only after the previous one has really finished, use setTimeout() instead:
// define a self-repeating function...
function ganttEach() {
$("td.gantt").each(function() {
// ...
});
window.setTimeout(ganttEach, 1000); // calls itself again in one second
}
// ...initiate self-repeating function
ganttEach();
You should probably include some way to stop the endless repetition here as well, like introducing a flag that's checked before the setTimeout() call.
You can run your check every few milliseconds, say 50ms, using setInterval
window.setInterval (function () {
// do checks here
}, 50);
You might end up using a lot of CPU power if your checks are too frequent, or too complicated.
It is possible, with setInterval. My advice would be to select the element outside of the repeating function so as to minimize the overhead.
An infinite loop would lock the browser UI, as it is a single threaded environment. Set interval, however let you add actions to the UI stack which will be executed after a given period of time. You can specify this period in the second parameter of setInterval.
// select the element outside
// to minimize overhead
$gantt = $("td.gantt");
// define a repeating action
setInterval(function() {
$gantt.each(function() {
if($(this).hasClass("oper")) {
//execute a serie of functions
}
if($(this).hasClass("preop")) {
//execute a serie of functions
}
});
}, 100); // repeat interval: 100ms
I am not sure exactly what you are trying to do, but have you tried setInterval? It will keep running if that is what you really want.
window.setInterval(function () {
// add your jQuery here
}, 100);

Categories