2007-03-20

ECMAScript : RendezVous mechanism

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

ECMAScript Quiz

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.