Unresponsive Firefox Scripts and RUZEE.Borders

There are quite some complaints and solutions about Firefox displaying annoying “unresponsive JavaScript detected, continue?” messages.

If you have scripts that simply sometimes take longer to do their stuff, you shouldn’t however force your visitors to change the default JavaScript “timeout” value as proposed by the tips linked above. You should consider doing parts of your calculations asynchronously and give free the one and only JavaScript execution thread for some milliseconds. Firefox will be happy in this case and won’t display the annoying message anymore, even when configured with its default settings.

I did that with the latest version 0.12 of RUZEE.Borders. Let’s have a look at the main rendering loop:

render:function(onfinished){
  if(onfinished) RUZEE.Borders.onfinished=onfinished;
  var start=new Date().getTime();
  for(rule in RUZEE.Borders.mappings){
    var e=RUZEE.Borders.cssQuery(rule);
    var b=new RUZEE.Borders.Border(RUZEE.Borders.mappings[rule]);
    delete RUZEE.Borders.mappings[rule];
    b.calc(e);
    // if we are rendering for more than 3 seconds, give Firefox some time to get
    // rid of the "unresponsive script" message.
    if(new Date().getTime()-start>3000){
      setTimeout('RUZEE.Borders.render()',0);
      return;
    }
  }
  RUZEE.Borders.renderCalcs();
  if(RUZEE.Borders.onfinished) RUZEE.Borders.onfinished();
}

In the beginning, the script saves the current time. Then it iterates over all rules inside the mappings hash table. It removes each rule on calculation (b.calc(e) and the delete before that line). Nothing really exciting up to that point. But hey, now comes the trick: Inside the loop the time is checked how long the loop is already running. If that is more than three seconds, the loop will hibernate and schedule it’s executing after 0 seconds – so most probably right after the return statement.

Firefox sees that a script was running for something like 3 seconds and doesn’t realize that the script continues afterwards. The trick works, because the mappings hash table gets emptied more and more with each iteration. Once we are done, i.e. all mappings have been processed, the calculations finally get rendered (renderCalcs()) and the onfinished callback gets called.

Wait… onfinished callback? Well, the render method won’t block until all mappings have been calculated. It will return after something like max. 3 seconds, the rest will be calculated “in the background” via the “setTimeout deamon”. That’s why you have to use a callback that gets invoked once the calculations and renderings are finished – you can’t rely on the return of render anymore! Have a look at the “Internet Explorer Blank Page trick” in the Tips and Tricks section of the RUZEE.Borders page.

The method described here can be used for every “iteration takes too long in Firefox” problem – although the solution feels like Windows 3.0 programming, it’s kind of clean – hope you like it ;-).