• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 940
  • Last Modified:

setTimeout & class methods

Im using Javascript under FireFox and IE6.  I just discovered (a bug?) within the browsers. I have a class function that is trying to use a setTimeout to make a call back using "this.MyMethod(1)":

function MyClass()
{
  this.id = arguments[0];
  this.ShowProgressBar = ShowProgressBar;
}

function ShowProgressBar (val)
{
   if (this.id == 1)
      tmrProgressBar = setTimeout(function(){this.ShowProgressBar(-1);},1000);

   alert(this.id+" "+val);
}

x = new MyClass(1);
x.ShowProgressBar();

=====

I've tried modifying the setTimeout to be a string or just a plain function (ie. setTimeout(this.ShowProgressBar, 1000))  It just doesnt seem that JS passes the original this object to the callback function.  How do i get around this?  I need this to be working and i need to pass variables to the callback function.

Tx
0
ljaques
Asked:
ljaques
  • 3
  • 2
  • 2
1 Solution
 
josephshi123Commented:
try this:

function ShowProgressBar (val)
{
   var thisObj = this;
   if (this.id == 1)
      tmrProgressBar = setTimeout(function(){thisObj.ShowProgressBar(-1);},1000);

   alert(this.id+" "+val);
}
0
 
ljaquesAuthor Commented:
Hi Joseph.  Thanks for the assistance.  I tried what you said in the following:

function aa()
{
      this.vv = vv;
      this.x = 22;
}

function vv()
{
      var MyObj = this;
      setTimeout(function(){MyObj.cc(33);},1000);

}


function cc(val)
{
      alert(val+" "+this.x);
}

b = new aa();
b.vv();

IE complains about Object does not support this property or method at the "setTimeout" line.  It seems it doesnt like the MyObj.cc(33) bit.  Firefox also fails.
0
 
josephshi123Commented:
function aa()
{
     this.vv = vv;
     this.x = 22;
     this.cc = cc; // <-- add this line
}
0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
dakydCommented:
The issue is that by the time setTimeout is called, the ShowProgressBar function has returned.  That means that "this" no longer points to the object whose method you called, but instead now points to the global window object.  In your case, since ShowProgressBar() is defined globally, the function is still called, though you don't get the results you want.

Joseph's post had the right idea (I think) - since you can't use "this", you need a variable that will point to the object whose method you're calling.  That way, you can can use that variable and be on your way.  The only problem was that he made that variable local, so you're back to the same problem as before - by the time the setTimeout executes, the function has returned & the local variable is no longer in scope.  Make the variable global, and that should resolve your issue.

The only catch is that if you can't run multiple versions of this, since that global variable will get over-written.  You *can* make it a global array/hash, but that gets messy.

<script type="text/javascript">
function MyClass()
{
  this.id = arguments[0];
  this.ShowProgressBar = ShowProgressBar;
}

function ShowProgressBar (val)
{
  globalObj = this; // notice no "var", so this variable is global
  if (this.id == "someId")
    tmrProgressBar = setTimeout("globalObj.ShowProgressBar('someVal');",1000);

  alert(this.id+" "+val);
}

x = new MyClass("someId");
x.ShowProgressBar();
</script>

One other thing I'd suggest - there is a setInterval() function that might also be helpful here.  It works just like setTimeout(), except that it will call the function that's passed as the first argument repeatedly at a set interval, rather than just once like setTimeout.  Particularly in this case, where you're using setTimeout for the same function call repeatedly, it might make some sense to use that.
0
 
ljaquesAuthor Commented:
dakyd, josephs solution worked for me even though it was using a local variable.  Is this because some "thing" is still pointing to the local thisObj (setTimeout is still using it) that it doesnt get released? Or was this just a coincidence / there wont be a consistency?  The global idea sounds interesting but it does sound a bit annoying to implement.  I will do just that once i have your answer.
0
 
dakydCommented:
No, it's not a coincidence, more like a screw up on my part.  Basically, I need to get my eyes checked ... =p

I'm used to passing a string to setTimeout as the first argument, and when I was testing joseph's solution, I automatically made it a string.  When you do that, then what I was saying holds - the local variable is out of scope at execution time, so you get an error.

However, if you pass an actual function object as the first argument, then there is a binding that happens when creating the function object.  In essence, you *can* implicitly reference the local variable, even though the showProgressBar() function has already returned by the time the function you pass to setTimeout is executed.

Hope that makes sense, but if not, ask away.
0
 
josephshi123Commented:
This is so-called "Javascript Closures"

A "closure" is an expression (typically a function) that can have free variables together with an environment that binds those variables (that "closes" the expression).

http://jibbering.com/faq/faq_notes/closures.html
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 3
  • 2
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now