2007-03-20
A common need when I program some concurrency stuffs in ECMAScript is to
be able to tell when the last event of a series of events happened.
The dumb way of doing things is to set a variable to true
each time an event occur. If all variables are true I know that all events have occured.
This is not a very elegant technic and that generate a lot of code.
I was thinking about a more convenient solution called "RendezVous". I have called
this function a "RendezVous" in memory of ADA language (RIP) that provide a syncronisation
mechanism of the same name. Here is my solution :
function rendezVous()
{
// number of functions : shared between decorator
var nbf = (arguments.length-1)/2;
// the last one is the callback
var callback = arguments[arguments.length-1];
for(var i=0;i<arguments.length-1;i=i+2)
{
var obj = arguments[i];
var func = arguments[i+1];
// replace the old function by a decorated function
obj[func]=rendezVousDecorator(obj,func,obj[func],
function(){--nbf;return nbf;},callback);
}
}
function rendezVousDecorator(obj,func,old,dec,callback)
{
return function()
{
// put the old function back
obj[func]=old;
// run the old function
obj[func]();
// decrement the counter and launch
// the callback if it's the last function
if(dec()==0)
callback();
}
}
// tests
var out = document.getElementById("out");
var obj1 = {func1:function(){out.innerHTML+="func1, "}}
var obj2 = {func2:function(){out.innerHTML+="func2, "}}
func3 = function(){ out.innerHTML+="func3, " };
// I construct the "RendezVous"
rendezVous(obj1,"func1",obj2,"func2",this,"func3",
function(){out.innerHTML+=" Rendez vous !!!\r\n"});
// I launch the "RendezVous" functions
obj1.func1();
obj2.func2();
obj1.func1();
// The "RendezVous" callback must be launched after this one
func3();
This "RendezVous" mechanism can be very useful with Ajax
calls. For the sake of simplicity I don't have use these.
Run this code
2007-03-05
I have learn a little bit with ECMAScript scope these days and I want to share it with you.
I am programming with Flash and there is a lot of concurrency problems that need to be resolved.
Closure are good tools for solving some of these issues in an elegant way.
The game here is to guess the result of each script that stands in a simple HTML page.
This will also works with Flash, the output will be certainly the same:
Question 1
funcs = Array();
strings = Array("hello","world");
for(var i=0;i<strings.length;i++)
{
var str=strings[i];
funcs[i]=function(){alert(str);}
}
for(var i=0;i<strings.length;i++)
{
funcs[i]();
}
Show the answer.
This code outputs 2 times the same string : "world". It's because the anonymous function copy
the reference on the variable "str" but not copy is value. A elegant solution to this
problem is to use a closure maker. The function "alertMaker" receive the "str" string by copy, and the
anonymous function capture this argument in this scope.
function alertMaker(str){
return function(){alert(str)}
}
for(var i=strings.length-1;i>=0;i--){
funcs[i]=alertMaker(strings[i]);
}
for(var i=funcs.length-1;i>=0;i--){
funcs[i]();
}
Question 2
var obj = {h:"hello",w:"world",f1:function(){
alert(this.h);
var f2 = function(){
alert(this.w);
}
f2();
}
};
obj.f1();
Show the answer.
This one outputs the "hello" string but fails to display the "world" string. It's because in the f2 scope
the this keyword is not a reference on obj anymore. The this keyword
available in the f2 scope is now a reference on f1. To get this script working as wanted
you have to write it this way:
var obj = {h:"hello",w:"world",f1:function(){
alert(this.h);
f1Parent = this;
var f2 = function(){
alert(f1Parent.w);
}
f2();
}
};
obj.f1();
Question 3
window.world = function world(){ alert("BUZZ!")};
Function.prototype.world=function(){alert('world')};
Function.prototype.hello1=function(){alert('hello'); this.world();};
Function.prototype.hello2=function (){alert('hello');}
function helloWorld1(){arguments.callee.hello1();}
function helloWorld2(){arguments.callee.hello2(); this.world();}
helloWorld1();
helloWorld2();
Show the answer.
"helloWorld1" outputs two strings : "hello" and "world". helloWorld2
outputs two strings again but "hello" and "BUZZ!".
- The call of
world in hello1 uses the "Function.prototype.world" function because the caller is the "helloWorld1" itself.
- The call of
world in helloWorld2 uses "window.world" because the caller is the root window Object.