31 January, 2006

the wonderfulness of eval( ) and setTimeout( )

If have been diggin' into delaying code execution without context loss.
One knows that when using setTimeout, you loose the context you where in when calling that function.
The idea is to make javascript take a picture of the context, wait, wait, then execute some code in that memorized context. Plus, I want the way to do that being easy to understand and natural to write.
So I came up with a couple of functions, each with a certain role to play:
1. We need a global variable that stack the contexts and unstack them when time is out!
2. We need an easy way of writing code to be executed.
The solution here is to encapsulate the code within an anonymous function that we will call when time is out within the unstacked context! Wonderful, isn't it ?
And here comes the code:

//A function that returns unique IDs
//(it's like a class with a class attribute)
getId = function() {
if (!this.id)
this.id = 0;
this.id++;
return this.id;
}
//A function that acts like a stack (registers objects by unique IDs)
registerObject = function(objType, id, o) {
if (!this.objById)
this.objById = new Array();
if (!this.objById[objType])
this.objById[objType] = new Array();
if (this.objById[objType][id])
alert('Object already registered with this ID!!');
else
this.objById[objType][id] = o;
}
//A function that unstacks the registered object with a given ID
getObjectById = function(objType, id) {
return this.objById[objType][id];
}
//We use objType for our stack to be reusable
//in other parts of our program
//This way, the stack can hold several object in different use contexts
//A test class to be instantiate
testClass = function() {
//Attribute
this.phrase = 'Cool! The context is saved!';
//Method with reference to the class object
this.doSomething = function() {
alert(this.phrase);
}
}
//The function that delay code execution
delay = function(code, contexte, time) {
//We provide a unique ID for our context object to be memorized
var id = getId();
//We register our context object in the stack
registerObject('delay', id, contexte);
//When time is out, the code will be evaluated within the stacked context
setTimeout('eval('+code+'.call(getObjectById(\'delay\','+id+')));', time);
}
//Then we run the test!!
//Instantiate the test class
var myTest = new testClass();
//Call a method right now
myTest.doSomething();
//Call the same method in 5 secondes
delay(function() { this.doSomething(); }, myTest, 5000);

And the magic operates!