Need help understanding why javascript object property is undefined - javascript

I need help understanding why the following piece of code returns an undefined object property:
var count = 0;
var intervals = {
collection : []
}
intervals.collection[0] = function () {
this.timer = setInterval(function(){
count++;
$("p").html(count);
}, 1000);
}();
if(typeof intervals.collection[0] === "undefined") {
$("span").html("undefined");
}​
Working Example: http://jsfiddle.net/tvaQk/8/
Basically, I'd like to be able to keep a collection of setIntervals that I can reference later so I can loop through and clear. I was thinking I'd be able to loop through the intervals.collection array and do something like:
clearInterval(intervals.collection[0].timer)
but cannot since intervals.collection[0] is undefined

You forgot the return.
intervals.collection[0] = function () {
return this.timer = setInterval(function(){
--^^^^^^--
Notice this refers to window, I'm not sure if adding a property timer to the window element is what you were expecting actually. Otherwise, you shall return the interval id to not clutter the global window scope.
A short way to being able to access the timer using intervals.collection[0].timer is by creating an Object instead:
intervals.collection.push({
timer: setInterval(function(){
count++;
$("p").html(count);
}, 1000)
});
console.log(intervals.collection[0].timer);
I used Array.push in this example to add the newly created interval as the last element of the array.

Maybe you're complicating things... What I would do is just push the interval straight into the array.
intervals.collection.push( setInterval(function(){...}, 1000) );
You don't need an IIFE to execute the interval, when you push it into the array it'll execute as well.
Then you can clear them all like:
intervals.collection.forEach(function( timer ) {
clearInterval( timer );
});

You could simply write it like this:
intervals.collection[0] = setInterval(function(){
$("p").html(++count);
}, 1000);

You're assigning a self-invoking function, which means you assign the return value of the function. The function itself doesn't return anything therefore it's undefined.

Related

call clear interval function from another function without pass the interval variable

I need to clear an interval from another function
window.onload = function(){
var interval = null;
interval = setInterval(function(){
myFunction();
}, 1000);
function stop(){
clearInterval(interval);
}
}
without pass the interval variable
stop();
But I cannot make it working: when I call stop(); the interval continues...
How can I do?
There is an unexpected window.stop function which preexists yours.
That's another proof that global variables/functions are evil.
It might be this function which gets invoked instead of yours, depending on when the script is loaded.
Try to put your function in an object to protect namespaces:
It works in the StackOverflow fiddle:
var i = 0;
function myFunction() {
i++;
console.log(i);
}
var interval = null;
interval = setInterval(function() {
myFunction();
}, 1000);
var myObject = {
stop: function() {
console.log("stopping");
clearInterval(interval);
}
};
<button onclick="myObject.stop();">stop</button>
In the faulty jsFiddle, you get things in iframes, meaning window element is not the same. That's why your function is not invoked. That gives you that kind of errors:
You can put your script in the html to get it working:
jsfiddle
You could simply put something like var interval = null; at the beginning of the JavaScript outside of a function.
It's all about variable scope. A variable defined inside of a function is only available within that function. A variable defined outside of a function or object will be available globally to all functions.
What is the scope of variables in JavaScript?
I found also this solution working:
stop = function(){
clearInterval(interval);
}
The presumption on the answer you've checked as solution is wrong.
Your stop function wouldn't be working regardless of the fact that there are browsers supporting load stop command programmatically.
This [stop] command is a window property and can be deleted and\or be overwritten by a simple variable declaration or by a function with the same name anywhere on the script.
The reason you are not being able to call the stop function ( from the outside ), is because it's a closure.
Regards.
p.s.:
throwing it up on global scope will make it work, visit you fiddle
var i=0;
function myFunction(){
i++;
$('i').html(i);
}
interval = null;
interval = setInterval(function(){
myFunction();
}, 100);
stop = function(){
clearInterval(interval);
}

Javascript: setInterval isn't removed when nullifying an object

The setInterval function keeps running even though the object is nullified, should I change the setInterval var to null first or should I do something else? Also, will the GC remove the object even if the setInterval is still running?
Test = function(){
function start(){
// setTimout for controllable FPS
var loop = setInterval(function(){
console.log("TEST");
}, 1000);
}
start();
};
var test = new Test();
setTimeout(function(){
console.log("DIE!!");
test = null;
}, 2000);
JsFiddle
the value returned by setInterval is just a number that used to identify the reference to the interval. you can't just null it - you need to call window.clearInterval on the reference.
there's a few other things that don't make sense in the code you posted. for example, you're declaring a function in a function then just calling it once. i think this is probably closer to what you want:
var Test = function(){
this.start();
}
Test.prototype = {
loop : null,
start : function(){
this.loop = window.setInterval(function(){
console.log('TEST');
}, 1000);
},
stop : function(){
window.clearInterval(this.loop);
}
}
var test = new Test();
window.setTimeout(function(){
test.stop();
}, 5000);
That'll run the interval 5 times.
FWIW, the GC isn't really involved here. As long as there's a reference to any variable, it won't be collected.
HTH

Confused with set timeout

I'm really confused how these work...the timeout does not seem to keep running it calls begin_anim once and then thats it....
So am hoping someone can see where i went wrong and explain how I implement this?
This is my code:
//test data:
//type = 'up';
//div = document.getElementById('theid');
//marginL = -400;
function timeout_begin(type,div,marginL){
setTimeout(begin_anim(type,div,marginL),1000);
}
function begin_anim(type,div,marginL){
if(type == 'up'){
if(marginL >= '-200'){
if(marginL > '-200'){
div.style.marginLeft = '-200px';
}
return false;
}
marginL += 2;
div.style.marginLeft = marginL+'px';
}
return false;
}
Hope you can help!
You're looking for setInterval!
Also, it's probably better to pass an actual function in, and you can hold a reference to the loop so you can stop it running later if you want to:
var animationLoop = setInterval(function () {
begin_anim(type, div, marginL);
}, 1000);
clearInterval(animationLoop); // This would then stop the loop.
First, you want setInterval, not setTimeout
Second, you'll pass a reference to a function, not a call to a function. Something like:
function timeout_begin(type,div,marginL)
{
setTimeout(
function() {
begin_anim(type,div,marginL);
},
1000
);
}
setTimeout is supposed to call the function only once.
if you want to call the method repeatedly use setInterval(function(){}, 1000/*duration*/)
setTimeout is only expected to execute the function once after the given timeout. See the documentation here: http://www.w3schools.com/jsref/met_win_settimeout.asp
You're probably looking for setInterval (http://www.w3schools.com/jsref/met_win_setinterval.asp) which executes the code at the interval you set until clearInterval is called.

Calling nested function from nested setInterval in an Object Namespace environment

I have this following piece of code:
var stats = {
....,
checkExistance :
function(url){
var newUrl = url.substring(0, url.lastIndexOf("/")) + "/asyncCheckChartExistance";
var xhrObj = stats.getXhr();
var poolInterval = setInterval("poll()", 100);
function poll(){
xhrObj.open("GET", newUrl, true);
xhrObj.send(null);
xhrObj.onreadystatechange = function(){
if(xhrObj.readyState === 4 && xhrObj.status === 200){
if (xhrObj.responseText.length === true){
console.log("Exists!");
clearInterval(poolInterval);
} else {
console.log("Not Yet!");
}
}
}
}
},
}
I created the stats namespace. In this namespace I'm trying to create a function which polls the server every second. I should access this function this way: stats.checkExistance(myUrl).
However it seems that the setInterval function is not able to see the poll() function. I know that this is normal behavior taking in consideration that these are nested inside another function.
If I were to write this in the Global namespace there would be no problem but I'm interested to make this work in this kind of namespace. Any ideas? Thanks!
when you pass a string to setInterval, it runs in the global scope, by default, where poll would not be defined since it only exists in the scope of the checkExistance function.
To fix the issue, pass an anonymous function to setInterval instead:
var poolInterval = setInterval(function () {
poll();
}, 100);
Passing an anonymous function is usually the best idea as it allows you to write any javascript expressions/statements for the interval instead of just calling one function.
When you pass a string to setInterval, that string is interpreted as global code, and since poll is not a global function, a reference error is thrown.
However, you can pass a function reference instead of a string, and since the poll function is available in the scope in which the setInterval invocation is made, you can just write this:
var poolInterval = setInterval( poll, 100 );
var stat = {
say: function(name){
function doit(){
console.log(name);
}
setInterval(doit, 1000);
}
};
stat.say("hi");​​​​​​​​​​​
A simple demo to show how. You will see "hi" every second.

Javascript/ECMAScript Garbage collection

Consider the following code (you can just put this in the developer console in Chrome and check).
var obj = {
f: function () {
var myRef = this;
val = setTimeout(function () {
console.log("time down!");
myRef.f();
}, 1000);
}
};
If I then run
obj.f();
to start the timer, I can see every second "time down!"
If I then run
obj = null;
The timer still fires.
Just curious why doesn't garbage collection clear out the timer? The scary thing is that it appears that there is no way to delete the timer now - am I correct?
My guess is that technically window still holds a reference to the object still consequently the object stays in memory. I've experienced this problem in another ECMA based language (Actionscript) and built a library for handling it, but sort of thought Javascript would take a different approach.
obj is not garbage collected because the closure that you pass to setTimeout must be kept around in order to be executed. And it, in turn, holds a reference to obj because it captures myRef.
It would be the same if you passed that closure to any other function that kept it around (for example in an array).
There is no way to delete the timer now, without horrible hacks1. But this is pretty natural: it's an object's job to clean up after itself. This object's purpose is to infinitely fire a timeout, so that object clearly intends to never clean up after itself, which might be appropriate. You can't expect something to happen forever without using up at least some memory while it does so.
1 Horrible hack: since timer IDs are just integers, you can loop from, say, 1 to 1000000000 and call clearTimeout on each integer. Obviously this will kill other running timers!
In response to K2xL's comment.
A minor adjustment of your function and it does behave like you suggest. If obj is given a new value the if will fail, the propagation will stop, and the whole lot can be garbage collected:
var obj = {
f: function () {
var myRef = this;
if(myRef===obj){
val = setTimeout(function () {
console.log("time down!");
myRef.f();
}, 1000);
}
}
};
I'd prefer a slightly flatter structure, you can skip the object container and rely just on a standard closure:
(function(){
var marker={}
window.obj=marker
function iterator(){
if(window.obj===marker){
setTimeout(iterator,1000)
console.log("time down!")
}
}
iterator()
})()
Note that you can use any object you desire for marker, this could easily be a document element. Even if a new element with the same id is erected in its place the propagation will still stop when the element is removed from the document as the new element is not equal to the old one:
(function(){
var marker=document.getElementById("marker")
function iterator(){
if(document.getElementById("marker")===marker){
setTimeout(iterator,1000)
console.log("time down!")
}
}
iterator()
})()
List item
Of course the timer still fires; you're recursively calling it inside the nested function with myRef.f.
Your guess was that the window holds a reference to obj. That is true, however, that's not why setTimeout is recursively called, nor what can be done to cancel it.
There are a few ways to provide for timer clearing. One way would be to pass in a condition function in the beginning.
To stop the timer, merely call clearTimeout and then don't recursively call setTimeout. A basic example:
(Identifier val is created as a property of the global object. Always use var!)
var obj = {
f : function (i) {
// (GS) `this` is the base of `f` (aka obj).
var myRef = this;
var timer = setTimeout(function () {
if(i == 0) {
clearTimeout(timer);
return;
}
console.log(i, "time down!");
myRef.f(--i);
}, 1000);
}
};
obj.f(4);
Moving a step up from that, an isDone method can provide more featureful check with refs passed back an forth. The setTimeout can be changed to setInterval.
var obj = {
f : function (i, isDone, animEndHandler) {
var timer = setInterval(function() {
console.log(i, "time down!");
if(isDone(--i)) {
animEndHandler({toString: function(){return"blast off!"}, i: i});
clearInterval(timer);
}
}, 1000);
}
};
function isDone(i) {
return i == 0;
}
function animEndHandler(ev) {
console.log(""+ev);
}
obj.f(3, isDone, animEndHandler);
The garbage collector doesn't clear out the timer function because something in the implementation of setTimeout() maintains a reference to it until you call clearTimeout().
You are correct that if you do not clear it and drop the reference to the value returned by "setTimeout()" then you have introduced a "memory leak" (in that the timer function cannot be removed).

Categories