Javascript Madness Intro

Javascript Madness: The Javascript Sleep Deficiency

Jan Wolter
Feb 17, 2009

In C (or Perl or PHP or zillions of other languages), if you want to pause the execution of your program for three seconds, you call the system function sleep(3). If you make this call, execution pauses for three seconds, and then resumes with the next statement after the sleep call. Other languages call this pause() or wait(). It's very handy.

Javascript, for some unaccountable reason, hasn't got a sleep function.

Restless Sleep

If you google for "javascript sleep", you'll find a hundred web sites where people people propose something like the following, usually with lots of added flab:
    function sleepStupidly(usec)
    {
	var endtime= new Date().getTime() + usec;
        while (new Date().getTime() < endtime)
	    ;
    }
DO NOT DO THIS! This is called spinning or busy waiting, a habit never to be indulged in except in circumstances unlikely ever to be encountered by a Javascript programmer. You are chewing up processing time on the user's computer. Very probably you will lock up the browser for as long as the sleep lasts, plus you will certainly slow to a crawl anything else that may be running on the user's computer. If your computer has no operating system and never runs anything other than your program, then you may spin to your heart's content. Otherwise, shun it or lose all accumulated guru points.

Proper sleep functions don't spin. They simply suspend execution of your process thread for the requested number of seconds, freeing up the CPU to do other things. When you write a program to run on a multi-processing system, your program is going to be part of a community of programs. Not spinning is basic good citizenship.

People have also tried tricks like starting a synchronous XMLHttpRequest with a timeout, but I understand this doesn't work right in all browsers. I don't really care if it does or not. It's too much of a horrible kludge for me to even want to try it. (There is a good discussion of sleeping with XMLHttpRequest and Java applets here.)

Time Out for Sleep

The solution that can actually be made to work correctly across all platforms is to use the Javascript setTimeout() function, but I warn you in advance, it's pretty danged horrid too.

Let's consider an example. We have a function init() that calls a function called draw(). The draw() function is going to do a whole lot of document.createElement() kind of stuff to draw some very complicated stuff on the screen. It takes a while, and some browsers, like Firefox, won't render anything while the Javascript is still running. To keep the user from dieing of boredom while watching a blank screen, we'd like to pause for a half second halfway through draw() so the browser can render some of the stuff we've put up, and the user can start exercising his eyes looking at it.

If we had a sleep function, we'd just stick it into the middle of the draw() function, wherever seems good to us:

    function init()
    {
	:
	// initialize data structures
        :
        draw();
        :
	// set up event handlers and get things going
        :
    }

    function draw()
    {
	:
	// draw the first stuff
        :
        sleep(500);
        :
	// draw the rest of the stuff
        :
    }

But we don't have a sleep function. We only have setTimeout. When you call setTimeout(func, usec) it just returns and continues on to the next statement immediately, but after usec milliseconds a call to the function func will be made. So, we could rewrite draw() like this:

    function draw()
    {
	:
	// draw the first stuff
        :
	setTimeout(draw2, 500);
    }

    function draw2()
    {
        :
	// draw the rest of the stuff
        :
    }
So, we've split the old draw() function into two. The first one does the first half of the work, and then we call setTimeout to schedule the function that does the second half of the drawing to get called in half a second.

This is grieviously inelegant. We've split a function that logically belonged together into two functions for no good reason. Probably we'll have to pass a lot of local variables across as function arguments. But it has a worse problem than inelegance: it doesn't really work right.

When you run init(), it does it's initialization and then calls draw(). The first half of the drawing is done, and the second half of the drawing is scheduled to be done in half a second. But long before that timeout triggers, the draw() function returns and the part of the init() function after the call to draw() executes, before the second half of the drawing has even started. This is disasterous! We need to somehow delay the return to init() until after the execution of draw2() has been completed.

How can this be done? Only one way I can think of. We need to split the init() function just as we split draw() before:

    function init()
    {
	:
	// initialize data structures
        :
        draw(init2);
    }

    function init2()
    {
        :
	// set up event handlers and get things going
        :
    }

    function draw(callback)
    {
	:
	// draw the first stuff
        :
	setTimeout(function(){draw2(callback)}, 500);
    }

    function draw2(callback)
    {
        :
	// draw the rest of the stuff
        :
	callback();
    }
So now, init() calls draw() passing in a reference to the function init2() which is supposed to be called when draw() is complete. Then draw() sets a timeout to call draw2() with the name of calling program's callback program. draw() returns to init(), and init() exits and Javascript execution halts. We are now sleeping. When the timeout triggers, it runs draw2() which calls init2() after it was is done.

Let's just hope that there wasn't another function calling init(), because if there was, then that has to be split too.

In summary, the simple need for a half-second sleep in a low-level routine forces us to dramatically and senselessly restructure the whole program into a complex mess of callbacks that will cross the eyes of anyone trying to figure out our program in the future. Frankly, I'd prefer goto statements. Well, it's good for job security, if nothing else.

Example: Sleeping with Lights Out

Here's another example where I wanted a sleep() function. Flixel is a little "Lights Out" type game I wrote as a demonstration of Javascript programming. The original version was written in front of an audience, and was very simple, but I fiddled with the program a little more, adding a few more features, and the current version is at http://unixpapa.com/flixel. You can view source on that to see the complete program.

One of the features I added was the ability for the game to show a solution when you check some boxes at the bottom of the screen. I used a much slower program to pre-compute solutions for all grid sizes and stored their solutions in a big Javascript array. So "seed[h-1][w-1]" contains the solution for a grid of size h by w. It doesn't contain the complete solution. It just tells which cells to click in the top row. You can fill in the rest of the solution by going through the grid, one row at a time from top to bottom, clicking each cell that has a red cell above it.

So the first version of the program that I wrote to display the solution looked like this (slightly simplified for clarity):

    function solve()
    {
	initialize_grid();

	// Get the top line solution
	var topline= seed[h-1][w-1];

	// Fill in first line.  X's in topline indicate cells to click
	for (var i= 0; i < w; i++)
	    if (topline.charAt(i) == 'X')
		clickon(i,0);

	// Fill in the rest, clicking cells under red cells
	for (var j= 1; j < h; j++)
	    for (var i= 0; i < w; i++)
		if (is_red(i,j))
		    clickon(i,j);
    }
Pretty danged simple.

But on big grids this took a bit to run and it was boring watching an unchanging screen until the finished solution popped up. I thought it would be more fun to re-write the program so it was animated, so you could see each click happen.

If I had some sort of sleep() function, I could just put a call to it right after each call to clickon(). A simple change that doesn't do much to muddle up the original program.

But we don't have sleep(). We have setTimeout(). Re-writing the code animate with setTimeout() left me with this:

    function solve()
    {
	initialize_grid();

	// Get the top line solution
	var topline= seed[h-1][w-1];

	solvetop(topline,0);
    }

    // Fill in first line.  X's in topline indicate cells to click
    function solvetop(topline,i)
    {
	var delay= 150;

	if (topline.charAt(i) == 'X')
	    clickon(i,0);
	else
	    delay= 0;

        if (++i < w)
	    setTimeout(function(){solvetop(topline,i)}, delay);
        else
	    setTimeout(function(){solvegrid(0,1)}, delay);
    }

    // Fill in the rest, clicking cells under red cells
    function solvegrid(i,j)
    {
	var delay= 150;
	if (is_red(i,j))
	    clickon(i,j);
	else
	    delay= 0;

	if (++i >= w)
	{
	    i= 0;
	    if (++j >= h) return;
	}

	setTimeout(function(){solvegrid(i,j)}, delay);
    }
So the solvetop() function is basically the body of the first loop, and the solvegrid() function is the body of the second loop. There is some fiddling around in each to to do the incrementing of loop index variables correctly, and to have the second loop start when the first one finishes, and also to have delays only when we clicked a cell, not when we didn't.

This works just fine, but again we had to do a total restructuring of the code, and the result is an unreadable muddle, much harder to understand at a glance than the original version.

Co-Sleeping

Javascript 1.7 offers another alternative, which is a bit strange, but less of a mess than the setTimeout solution: coroutines.

This is sort of a different version of a function call. Instead of having the caller call the function once and not resume running until the function is done, the called function can be halted at various points during its execution, returning control to the caller at each point. It's sort of like setting breakpoints. When you write the function, you can put breakpoints in wherever you want. When it runs, control returns to the parent at each breakpoint. The parent can then do something, and then restart the child.

Here's a rewrite of my flixel solver using coroutines:

    function solve()
    {
	initialize_grid();

	// Get the top line solution
	var topline= seed[h-1][w-1];

        var gen= dosolve();
	animate(gen,150);
    }

    function animate(gen, step)
    {
	if (gen.next())
	    setTimeout(function() {animate(gen,step)}, step);
    }

    function dosolve()
    {
	// Get the top line solution
	var topline= seed[h-1][w-1];

	// Fill in first line.  X's in topline indicate cells to click
	for (var i= 0; i < w; i++)
	    if (topline.charAt(i) == 'X')
	    {
		clickon(i,0);
		yield true;
	    }

	// Fill in the rest, clicking cells under red cells
	for (var j= 1; j < h; j++)
	    for (var i= 0; i < w; i++)
		if (is_red(i,j))
		{
		    clickon(i,j);
		    yield true;
		}
	yield false;
    }
The good thing here is that the dosolve() function looks pretty much just like our original program. You can see the loops again. All that is changed is that we've got those weird yield statements in the places where we wanted to put our sleep() calls, right after each click on the grid. Those are our breakpoints.

The call to dosolve() that is made in the solve() function doesn't really run the program. Because dosolve() has these yield statements in it, it becomes a different animal, called a "generator", and when called it just returns a handle, which we store in the gen variable. To actually run the thing, we need to call gen.next(). This runs the generator to the next breakpoint, and returns whatever value was given after the yield statement, in this case just a flag telling if we are done.

To run this thing with a sleep at each yield we wrote the animate() function. It calls gen.next() to run the solver up to the first breakpoint, then, if we aren't done, it sets a time out to call itself to do the same thing in 150 milliseconds.

We could handle the termination differently. We could drop the final "yield false" from dosolve(), and just have animate() catch the exception that is thrown when we call gen.next() after dosolve() has returned. When it got that exception, animate() would return.

Either way, this works great and it doesn't make a complete hash of our original program.

But, alas, you probably can't use this technique. So far as I know (as of February 2009), the only browsers that support Javascript 1.7 are the ones based on the Gecko rendering engine. So it works in Firefox 2.0 (Gecko 1.8.1) and later. But it won't work in IE, Safari, or Opera.

There are other problems too. Suppose that instead of putting the yield statements after each call to clickon(), we put them inside clickon()? Sorry. Won't work. Only one level of function calling allowed here. The yield statements can't be in lower level functions.

So this wouldn't fix the problem we had in our first example, where we wanted to sleep() in a routine that was several calling levels down. We could put a yield into the draw() function to avoid splitting it, but we'd still have to split the init() function, The init() function would run the first half of draw(), then set a timeout to call init2(), which would run the second half of draw and then set up its event handlers. Any function that calls init() would have to be split too.

Conclusions

In my first example, we didn't really want to sleep half a second. We really just wanted to pause for however long it took the browser to catch up on rendering. It'd be nice if there was some function we could call to do that, and I'm not completely sure this doesn't exist at least in some browsers, in the form of an event fired when drawing is complete. But even if it were possible to do that, there are other circumstances where a sleep is desirable.

Maybe someday other browsers will implement Javascript 1.7, and coroutines will be generally available, but we've seen that that isn't really a fully satisfactory solution to Javascript's sleep deficiency.

It'd be nice if a sleep() or wait() function, or something that could be used the same way, would eventually appear in some standard, but I don't have a lot of hope for this. Even setTimeout() isn't actually standardized anywhere, and I have no idea if any existant standards committee would think that it or a sleep() function to augment it would fall under their purview.

So the bottom line is to avoid sleeping, if you can. If unavoidable, bite the setTimeout() bullet, and I'm sorry for you.