Skip to main content

Sleep sort in JavaScript and closures


     There is this "genius" sorting algorithm called "sleep sort". I don't know what came to my mind, but I decided "let's do it in javascript!". I thought that it won't take more than 2 minutes. Starting with such wrong and over confident assumption, it was a journey into the details of javascript which I had never bothered to pay much attention to. I learnt quite a few things on the way and will share the same in this blog.

     Here is the code which I wrote off :

1
2
3
4
5
array = [1, 9, 4, 8, 2, 3, 6];

for (i = 0; i < array.length; i++) {
    window.setTimeout ("console.log(array[i])", array[i]*1000);
}  


     What the code was supposed to do was, for each integer in the array, wait for those many seconds and then print that integer. So, the integer 9 will be logged into the console after 9 seconds while the code will log 2 into the console after 2 seconds.
      Here is what it logged into the console :
undefined
undefined
undefined
undefined
undefined
undefined
undefined
      What?! said I, totally baffled and and taken by surprise. I thought that there is some problem with the array so, in the for loop, I changed array.length to array.length - 1. Now, all the undefineds in the output were replaced by 6s.
      What was happening here? The line 4 in the code was telling the javascript engine that after array[i] number of seconds, evaluate the javascript code console.log(array[i]). So it was here, that all the logic was going for the toss then! In two shakes of a duck's tail, the for loop would come to a stop. Then, after a comparatively long time (1 second) the first timeout will get over and the code console.log(array[i]) will get evaluated. But wait! by this time, the value of i is 7 and each of those timeouts will end up evaluating console.log(array[7]). And we know that as the array contains only 7 elements and array[7] means the 8th element in the array, there is no doubt that undefined will be logged into the console! Mystery solved!
      I read up a bit on the web about the setTimeout function and came to know that it is a bad practice to use a string as the first parameter of setTimeout for various reasons - mostly the association with eval . The syntax of setTimeout as given there is :
setTimeout (function_object, timeout_in_milliseconds, param1, param2, param3.....)
where the arguments after timeout_in_milliseconds are all optional. For example,
window.setTimeout(foo, 5*1000, 99, 42)
will make a function call to foo as foo(99, 42) after 5 seconds.
       So I changed the code to following :
1
2
3
4
5
6
7
8
9
array = [1, 9, 4, 8, 2, 3, 6];

function log_value_in_console (value) {
    console.log (value);
}

for (i = 0; i < array.length; i++) {
    window.setTimeout (log_value_in_console, array[i]*1000, array[i]);
}
       Here, right at the time of setting the timeout, line 8 was telling the javascript engine. "After array[i] seconds, execute the function log_value_in_console with array[i] as its parameter". Mind you, the value of array[i] will be calculated at the same time when the timeout is set. (after all, they are parameters passed to the function window.setTimeout and as usual, they will be evaluated when you are passing them).
       Quite as expected, this is what it logged into the console :
1
2
3
4
6
8
9
       Yey! It worked! But on the same page where I read about setTimeout, there was a note saying "passing additional parameters to the function does not work in Internet Explorer." Alas! Why did you guys at mozilla even add that then? Whatever...
       So now the problem was to do sleep sort with just two parameters to the setTimeout function. Basically, first argument needed to be a function which somehow knows what to log. Enter closures!
       What is this closure anyway? Frankly, I don't know :P. What I understand is "a closure is like a zombie, a भूत!" closure is formed when a function returns (gets done with its execution) but still its scope, that is its internal variables and arguments passed to it (think of it as kind of a stack frame of that function) are not cleaned by the garbage collector. Such a scenario typically happens when the function returns another function and returned function needs to access the internal variables / arguments of the outer function. Apart from the fancy name which notoriously is name of a mathematical property also, there is nothing complex or voodoo about closures.
       Using closures, I rewrote the code in following way, and as you might have guessed, it worked! :P
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
array = [1, 9, 4, 8, 2, 3, 6];

function make_logger_function (value) {
    function log_value_in_console () {
        console.log (value);
    }
    return log_value_in_console;
}

for (i = 0; i < array.length; i++) {
    window.setTimeout (make_logger_function(array[i]), array[i]*1000);
}
       When the execution comes to line 11, it evaluates both its arguments, the second, as we all know returns  the number of milliseconds timeout after which, the first argument (which is function) should be executed. So what does the first argument evaluate to? It returns a function, which knows what value it has to log. Here, we can see closure playing its role. The function make_logger_function takes a value to be logged into the console and returns a function which, upon called, logs that value in the console.

Comments