Jump to content

setInterval inside of setTimeout inside of object method


shadowayex

Recommended Posts

This will be kind of hard to explain. I have a object, and one of the fields is an array of objects of a different type. Let's call the different objects ObjectA and ObjectB.ObjectA:

function ObjectA{	var ref = this;	this.objectBs; // The array of ObjectB's	this.objMethod = function()		{			for (i = 0; i < objectBs.length; i++)			{				setTimeout("ref.otherMethod(" + i + ")", 1000);			}		}	this.otherMethod(index)		{			// Some set up code, and then:			setInterval(ref.objectBs[index].objMethod, 5000);		}}

ObjectB:

function ObjectB{	this.objMethod = function()		{			alert("I'm launched");		}}

The above doesn't work. I get an error about ref not existing.The goal I'm attempting to achieve is this:I want to launch a method of ObjectB every so many seconds, but I want the interval to be started for each items after so much time. The effect I'm looking for is so one of the objects to be doing the interval for so much time, then for the next one to join, and then the next one. I've got everything to work besides getting this interval to work (I've gotten them all to do the interval simultaneously successfully).I've tried multiple design ideas, and none of them have worked. The above is an example of one I've tried unsuccessfully. I'm open to rewriting it, so long as the basic idea is still there. I still want to set the intervals from within a for loop inside of ObjectA's method. The code that is being delayed by the setTimeout is multiple statements, the last of which is the setInterval, which is supposed frequently to launch a method in the specified ObjectB.I think I've explained this clearly enough, but if there are details that need clarified, I can try to clarify them.I'm pretty much out of ideas for this and would like some guidance getting something running.

Link to comment
Share on other sites

I'll answer some of this, since I got you pointed in this direction. In this.objMethod , ref would be a legitimately enclosed reference if it were not wrapped in quotes. Putting it in quotes changes the scoping rules again. Bothersome, isn't it? The reason is that the quoted string gets evaluated when the timeout function is executed; by that time, the reference is dead. When the string in constructed, the interpreter has no way of knowing that "ref" in quotes is supposed to be a function reference and not just random character data.I suspect the reference passed to this.otherMethod will work, since it is passed as a true reference, not a quoted string.The solution to the first method might be to an pass an anonymous function to setTimeout; the unquoted reference should be called in the anonymous function, and you'll still be able to enclose the value of i in there. Try it, anyway.

Link to comment
Share on other sites

I'll answer some of this, since I got you pointed in this direction. In this.objMethod , ref would be a legitimately enclosed reference if it were not wrapped in quotes. Putting it in quotes changes the scoping rules again. Bothersome, isn't it? The reason is that the quoted string gets evaluated when the timeout function is executed; by that time, the reference is dead. When the string in constructed, the interpreter has no way of knowing that "ref" in quotes is supposed to be a function reference and not just random character data.I suspect the reference passed to this.otherMethod will work, since it is passed as a true reference, not a quoted string.The solution to the first method might be to an pass an anonymous function to setTimeout; the unquoted reference should be called in the anonymous function, and you'll still be able to enclose the value of i in there. Try it, anyway.
I'm not sure if this is what you meant, but what I did was take the quotes out of the setTimeout line, so it now says something like:
setTimeout(ref.otherMethod(i), delay);

I get an error useless setTimeout call (missing quotes around argument?), but the first object gets the timeout and interval. In the loop, I placed an alert both above and below the setTimeout, I see the first alert once, but nothing else.

Link to comment
Share on other sites

I notice now that i is an implicitly global variable. I suspect when otherMethod runs, it's using the global value, whatever it turns out to be. I'm not sure what value it will have if you declare it to be a var in objMethod, but it's got to get us closer than what's happening now.Scope, scope, scope.

Link to comment
Share on other sites

I notice now that i is an implicitly global variable. I suspect when otherMethod runs, it's using the global value, whatever it turns out to be. I'm not sure what value it will have if you declare it to be a var in objMethod, but it's got to get us closer than what's happening now.Scope, scope, scope.
Adding var i; right before the for statement did not change anything. I'm assuming that's what you meant.
Link to comment
Share on other sites

What if, instead of passing the index to otherMethod, you passed the actual object?Something like:

this.objMethod = function() {   for (i = 0; i < objectBs.length; i++) {	  setTimeout(function() { ref.otherMethod(ref.objectBs[i]); }, 1000);   }}this.otherMethod(obj) {   // Some set up code, and then:   setInterval(obj.objMethod, 5000);}

Link to comment
Share on other sites

What if, instead of passing the index to otherMethod, you passed the actual object?Something like:
this.objMethod = function() {   for (i = 0; i < objectBs.length; i++) {	  setTimeout(function() { ref.otherMethod(ref.objectBs[i]); }, 1000);   }}this.otherMethod(obj) {   // Some set up code, and then:   setInterval(obj.objMethod, 5000);}

The reason I was passing the index was because whenever I try to access ref.objectBs[ i ] in objMethod's setTimeout(), I get undefined errors. Yet when I do it in otherMethod, it works fine. That was one of my designs that failed.
Link to comment
Share on other sites

Ah, wait. I think I know what the problem is. The for loop continues to run while it's waiting for the timeout to expire, so i will always be whatever the length of objectBs (or whatever i happens to be at when the first timeout expires). If you get rid of the timeout or set its delay to a very short time (like 1) it should work.I'm not sure how you'd fix that though.... :)

Link to comment
Share on other sites

Ah, wait. I think I know what the problem is. The for loop continues to run while it's waiting for the timeout to expire, so i will always be whatever the length of objectBs (or whatever i happens to be at when the first timeout expires). If you get rid of the timeout or set its delay to a very short time (like 1) it should work.I'm not sure how you'd fix that though.... :)
The delay is important. It is set to a certain time that it calculated in the for loop. It is different for each item.Edit: I think the issue with the undefined error with the array was that i was still -1. Even when the object was passed, i was still -1. So that didn't change anything.The issue seems to be that when i changed, it changed for everything. Must be like what DD said, the setTimeout() is being launched at a different time, and it's accepting the value of i at that current time, which was -1.I'm going to play around and see if I can figure out a solution based on this theory.
Link to comment
Share on other sites

Right. I understand that. I just wanted to illustrate my point. Does it work without the delay?
Yep, read my post above.Edit: Still no solution. It's definitely a scoping issue, though, as DD said.
Link to comment
Share on other sites

I was able to solve the problem using eval...not sure if it's the greatest solution though.

this.objMethod = function() {   for (i = 0; i < objectBs.length; i++) {	  eval("setTimeout(function() { ref.otherMethod(ref.objectBs["+i+"]); }, 1000);");   }}

Note: This is using my version of your code where the object is passed instead of the index.

Link to comment
Share on other sites

I was able to solve the problem using eval...not sure if it's the greatest solution though.
this.objMethod = function() {   for (i = 0; i < objectBs.length; i++) {	  eval("setTimeout(function() { ref.otherMethod(ref.objectBs["+i+"]); }, 1000);");   }}

Note: This is using my version of your code where the object is passed instead of the index.

As you said, it works. My code still doesn't work as expected, but it's due to other issues.Thanks for the solution, and if anyone finds a better one, let me know.
Link to comment
Share on other sites

Here's code to add a createDelegate method to functions:

Function.prototype.createDelegate = function(obj, args, appendArgs){  var method = this;  return function() {	var callArgs = args || arguments;	if (appendArgs === true){	  callArgs = Array.prototype.slice.call(arguments, 0);	  callArgs = callArgs.concat(args);	}else if (!isNaN(parseInt(appendArgs, 10))){	  appendArgs = parseInt(appendArgs, 10);	  callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first	  var applyArgs = [appendArgs, 0].concat(args); // create method call params	  Array.prototype.splice.apply(callArgs, applyArgs); // splice them in	}	return method.apply(obj || window, callArgs);  };}

Using createDelegate, you can create a copy of your function to run in a certain scope and pass it whatever you want. So you could replace this:eval("setTimeout(function() { ref.otherMethod(ref.objectBs["+i+"]); }, 1000);");with this:setTimeout(ref.otherMethod.createDelegate(ref, [ref.objectBs[ i ]]), 1000);That is telling setTimeout to execute the ref.otherMethod function, to run it in the scope of the ref object (so that this points to ref), and to pass it ref.objectBs, which will all get evaluated when that line executes instead of when the timeout fires.

Link to comment
Share on other sites

Here's code to add a createDelegate method to functions:...code...Using createDelegate, you can create a copy of your function to run in a certain scope and pass it whatever you want.
Ah, yes. Your famous delegate function. :) I forgot about that.Just out of curiosity, what is the purpose of the appendArgs parameter? What exactly does it do?
Link to comment
Share on other sites

I ripped that out of Ext, I didn't write that myself. The documentation for that is here:http://dev.sencha.com/deploy/dev/docs/?class=Function

appendArgs : Boolean/Number(optional) if True args are appended to call args instead of overriding, if a number the args are inserted at the specified position
Most of the code is for handling that. Without it, this is all you need:
Function.prototype.createDelegate = function(obj, args){  var method = this;  return function() {	var callArgs = args || arguments;	return method.apply(obj || window, callArgs);  };}

Link to comment
Share on other sites

I ripped that out of Ext, I didn't write that myself.
I see that, now. I just got done rifling through a bunch of examples and documentation for ExtJS. ExtJS looks incredibly powerful....
appendArgs : Boolean/Number(optional) if True args are appended to call args instead of overriding, if a number the args are inserted at the specified position
Most of the code is for handling that. Without it, this is all you need:
Function.prototype.createDelegate = function(obj, args){  var method = this;  return function() {	var callArgs = args || arguments;	return method.apply(obj || window, callArgs);  };}

So, basically useless for most applications, in other words?
Link to comment
Share on other sites

I remembered. It's mainly for event listeners. A panel has a "render" event, for example, so if I want to attach a listener to the render event and have it send additional parameters to the event handler in addition to the built-in event parameters, you would use appendArgs to append those arguments. e.g.:

Ext.getCmp('some-panel').on({  'render': render_handler.createDelegate(this, [p1, p2], true);});

Since the render event automatically sends the panel as a parameter, it would get called like this:function render_handler(panel, p1, p2)So it would have the original event handler parameter, the panel, plus the two additional parameters I sent with createDelegate.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...