I'm wondering why my variable, which is initially set to a value, becomes undefined when accessed from an interval.
Consider the following JQuery:
var TEST = {
DIR: null,
ITV: null,
Init: function () {
this.DIR = 1;
this.Action();
},
Action: function () {
console.log(this.DIR);
if (this.ITV == null)
this.ITV = setInterval(this.Action, 1000);
}
}
$(document).ready(function () {
TEST.Init();
});
The output of this code is as follows:
1
undefined
I understand the 1, because the value of TEST.DIR is set to 1 inside the Init function, which it still is when the Action function is called for the first time.
However, when this function is being called for the second and all other times from the interval TEST.ITV, TEST.DIR is undefined and I don't understand why.
See also this FIDDLE.
Can someone please explain to me what I'm doing wrong or what I'm overlooking?
Thanks!
this.Action is passed to setInterval as function w/o its context so this is not pointing to the instance of TEST any more but defaults to window. Try it like this:
var TEST = {
DIR: null,
ITV: null,
Init: function () {
this.DIR = 1;
this.Action();
},
Action: function () {
console.log(this.DIR);
if (this.ITV == null)
this.ITV = setInterval(function() {TEST.Action() }, 1000);
}
}
$(document).ready(function () {
TEST.Init();
});
http://jsfiddle.net/h44y81Lu/4/
thats because 'this' refers to the window in your set interval, thats why its undefined (there is no window.DIR)
A lot of people have issues with the 'this' keyword, check the MDN docs out
Related
I have an object that I have a few functions inside that I am using setTimout inside. I'm trying to clear the timeout using clearTimeout.. but I'm not hitting it right.
var ExpireSession = {
killSession: function () {
var TESTVAR2 = setTimeout(function () {
window.location.href = "error/expired.aspx";
}, 15000);
},
stopTimers: function (){
clearTimeout(ExpireSession.killSession.TESTVAR2)
}
}
Before 15 seconds I am triggering: ExpireSession.stopTimers(); but it does not stop it. Any ideaas what I am doing wrong here?
var TESTVAR2 is a variable that is local to the function it is declared within. It is not a property of an object.
If you want to access it as a property of an object, then you must define it as such:
ExpireSession.killSession.TESTVAR2 = setTimeout(function () {
(You might be able to make use of this depending on how you call the function).
Because JavaScript has functional scope, TESTVAR2 will only be defined within killSession. To reference it, you can set it as a property of ExpireSession:
killSession: function () {
this._TESTVAR2 = setTimeout(function () {
window.location.href = "error/expired.aspx";
}, 15000);
},
stopTimers: function () {
clearTimout(this._TESTVAR2);
}
Preety straight forward question, though I can't find the answer anywhere
I tried these two ways:
setInterval(function(){object/*or this*/.method()},500)
and
setInterval('object/*or this*/.method()',500)
setInterval in fact expects a method as the first argument, though there is an alternative syntax where the first argument can be a string of code (not recommended by most)
If you're having issues with that code, it may have to do with the scope of 'this'
setInterval(function(){this.method()},500)
In the above code, 'this' will refer to the closure itself, and wouldn't be the same as 'this.method' occurring outside of that closure. For example, the following would work:
function MyClass() {
this.thingy = 'yep this is a thingy'
}
var myClass = new MyClass()
// Will log 'MyClass yep this is a thingy'
setInterval(function() { console.log('MyClass', myClass.thingy) }, 1000)
Whereas the following will not work (presuming instantiating the object and calling foo()):
function MyOtherClass() {
this.thingy = 'also a thingy'
}
// Will log 'MyOtherClass undefined'
MyOtherClass.prototype.foo = function() {
setInterval(function() { console.log('MyOtherClass', this.thingy) }, 1000)
}
The second example will work if we get around using 'this' within the closure (presuming instantiating the object and calling bar()):
MyOtherClass.prototype.bar = function() {
var that = this
setInterval(function() { console.log('MyOtherClass', that.thingy) }, 1000)
}
Also be sure that setInterval is being passed the name of a function:
setInterval(someFunction, 500)
rather than executing a function as an argument
setInterval(someFunction(), 500)
This last line of code is usually a mistake, unless someFunction() returns a function itself ;)
The difference between your 2 ways for passing a function to setInterval is whether you want to pass your function as refrence of just copy of it. Allow me to explain it by example:
-1 Referring(demo):
var obj = {
testMethod: function () {
console.log('function (testMethod): intial output');
}
}
setInterval(function () {
obj.testMethod()
}, 1000);
obj.testMethod = function () {
console.log('function (testMethod): changed output');
}
when you run this code, the result 'll be execution of the modified version of testMethod. Because here you dont copy the function! Instead, you refer to it. So whenever function implementation is changed, the last modified version is executed.
-2 Copying(demo):
var obj = {
testMethod: function () {
console.log('function (testMethod): intial output');
}
}
setInterval(obj.testMethod, 1000);
obj.testMethod = function () {
console.log('function (testMethod): changed output');
}
Here all you do is you are passing a copy of the last defined version of the function testMethod to setInterval. So whatever changes you do to testMethod, the result of setInterval will not be changed.
Can someone please explain to me what is wrong with my code below? I am declaring a public variable and setting it to a setTimeout, and if not null, clearing the timeout before it gets set again. When I try to clear the timeout I get undefined so the timeout continues to run.
var usernameCheckTimeout = null;
$(document).ready(function(){
$("#username").on("keyup", function(e){
if($(this).val().length >= 6)
{
if(usernameCheckTimeout != null)
{
clearTimeout(usernameCheckTimeout);
}
usernameCheckTimeout = setTimeout(isUsernameAvailable($(this).val()), 1000);
}
});
});
function isUsernameAvailable(username)
{
$.ajax({
url : "/account/username-check",
method : "POST",
dataType : 'json',
data : {
'username' : username
}
}).done(function(data) {
console.log(data);
});
};
You do not need to do the null check also you need to create a closure around this, otherwise this will refer to not what you think this actually is.
var usernameCheckTimeout;
$("#username").on("keyup", function (e) {
if ($(this).val().length >= 6) {
clearTimeout(usernameCheckTimeout);
var that = this;
usernameCheckTimeout = setTimeout(function () {
isUsernameAvailable($(that).val();
}, 1000);
}
});
Some jsfiddle love like usual.
The timeout is being cleared. The problem is that you are calling your function immediately instead of passing the function to setTimeout.
setTimeout(isUsernameAvailable($(this).val()), 1000);
isUsernameAvailable($(this).val()) will be called and the result of this call will be passed to setTimeout.
You should instead pass a function which calls this function:
EDIT: As #Mark said, you also need to deal with this not being what you expect:
var value = $(this).val();
setTimeout(function(){
isUsernameAvailable(value)
}, 1000);
You have a couple of issues. The first issue, which is huge, is that you are executing isUsernameAvailable($(this).val()) immediately and passing the return value to setTimeout - you need to move this into an anonymous function so it does not execute until the anonymous function is called by the timeout:
usernameCheckTimeout = setTimeout(function () {
isUsernameAvailable($(this).val());
}, 1000);
the javascript timeout functions rely on numeric IDs to function. You should avoid testing for null or undefined or anything else, and instead test for a number:
// leave it as undefined
var usernameCheckTimeout;
...
if (typeof usernameCheckTimeout === 'number') {
clearTimeout(usernameCheckTimeout);
}
When I start my script I have this:
var my_great_masterpiece = new function ()
{
var self = this;
Then later in my script I have this:
response_xml: function ()
{
if (self.http_request.readyState == 4)
{
if (self.http_request.status == 404 && countXmlUrl <= 3)
{
countXmlUrl++;
self.realXmlUrl = xmlUrl[countXmlUrl];
self.request_xml();
}
if (self.http_request.status == 200)
{
self.xmlDoc = self.http_request.responseXML;
self.storage.setItem('domains_raw_xml', self.http_request.responseText);
self.main.peter_save_data();
self.timervar = setTimeout(function ()
{
// ########### Below line gives the error #############################
self.new_version_show_window();
}, 2000);
}
}
},
new_version_show_window: function ()
{
...
}
the error that I am getting is:
Error: self.new_version_show_window is
not a function
What am I doing wrong?
Thanks!
It is unclear from your code where new_version_show_window is defined. Maybe you could explicitly define it on self:
self.new_version_show_window = function () {
/* ... */
}
instead. Or you could define it in the local namespace and use it directly in the setTimeout call:
self.timervar = setTimeout(function () {
new_version_show_window();
}, 2000);
or simply:
self.timervar = setTimeout(new_version_show_window, 2000);
Because of closure, the variables declared in the outer function is also available in the inner function.
Edit
Thanks for posting the entire code. new_version_show_window is defined on this.main, so you must access it thusly:
self.timervar = setTimeout(function () {
self.main.new_version_show_window();
}, 2000);
It could be that self is a reserved word in JavaScript [1]. This could be causing you some problems so try naming the variable something different to start with.
[1] http://www.quackit.com/javascript/javascript_reserved_words.cfm
This is a problem of scope. new_version_show_window is only in scope in the construct in which is it called ( apparently a jQuery AJAX function of some sort). It will only be available to my_great_masterpiece if you define it outside the limited scope in which it now exists.
var Test = (function() {
return {
useSub: function () {
this.Sub.sayHi();
},
init: function () {
$(document).ready(this.useSub);
}
};
})();
Test.Sub = (function () {
return {
sayHi: function () {
alert('hi');
}
};
})();
Test.useSub(); // works
Test.init(); // explodes
Above I am trying to create a Test namespace and add an object Sub to it. I was doing fine until I tried using the object in jQuery. The error is "Uncaught TypeError: Cannot call method 'sayHi' of undefined". If there is a better way to do this, I am open to it.
Edit:
Obviously this was demo code. In my real application the solution that I went with because I think it is the most clear is this one:
var Namespace (function () {
return {
init: function () {
$(document).ready(function() {
Namespace.onReady();
}
},
onReady: function() {
alert('Now I am back in the Namespace scope. Proceed as planned');
}
};
})();
Edit2: All jQuery callbacks seem to require they are used in this manner or else the scoping is screwed up.
I think it is a scope problem. If you do
$(document).ready(this.useSub);
then this.useSub will be executed in the window scope (so inside the function, this refers to the window object) and there doesn't exist a Sub attribute.
Try:
init: function () {
var obj = this;
$(function(){obj.useSub()});
}
For some reason it does not work using $(document).ready(function(){obj.useSub()}); but it works with the $() shortcut.
Here is one way
var Test = {
useSub : function () {
Test.Sub.sayHi();
},
init: function () {
$(document).ready(Test.useSub);
},
Sub: {
sayHi: function () {
alert('hi');
}
}
};
in this line:
$(document).ready(this.useSub);
you're passing a reference to a function and the scope is lost- when the function runs, this no longer means Test.