Mozilla.com

  

One of the most common questions related to extension development is how to perform long-running operations in the background. If not done with care this can simply hang the application until the operation is complete. There are a couple of techniques.

Using setTimeout to schedule tasks

By far the easiest and safest way to perform work in the background involves splitting it into small slices of activity and then scheduling each to run. For example:

 function backgroundTask() {
   // Perform a small amount of work
 
   window.setTimeout(arguments.callee, 100);
 }
 
 window.setTimeout(backgroundTask, 100);

Here a function is scheduled to run every 100ms. Providing each run of the function takes a small amount of time the application will continue to be responsive. Once the function has completed whatever work it is performing it should just return early rather than reschedule the execution.

Queuing a task on the main thread

A simple way to spawn a task on the main thread, without using setTimeout, is to create a simple runnable object and dispatch it using the Thread Manager. Here is a handy function that can be used as a replacement for setTimeout:

function executeSoon(aFunc)
{
  var tm = Components.classes["@mozilla.org/thread-manager;1"]
                     .getService(Components.interfaces.nsIThreadManager);

  tm.mainThread.dispatch({
    run: function()
    {
      aFunc();
    }
  }, Ci.nsIThread.DISPATCH_NORMAL);
}

Since the function is run in the main thread, it can make the UI unresponsive. To workaround that limitation, you need to use a real thread and create a background task.

Creating a real thread

Using a real thread is far more difficult to get right. In particular there are lots of pitfalls where you can't touch the DOM and many other components from a background thread. Doing so can cause the whole application to crash or hang. When testing your code you should do so in a debug build so that you can see any thread-safety issues that are asserted in the console.

 backgroundTask = {
   run: function() {
     // perform work here that doesn't touch the DOM or anything else that isn't thread safe
   }
 }
 
 var thread = Components.classes["@mozilla.org/thread-manager;1"]
                        .getService(Components.interfaces.nsIThreadManager)
                        .newThread(0);
 thread.dispatch(backgroundTask, thread.DISPATCH_NORMAL);

This shows a simple object that has its run method called by a background thread that is created. There is more detail on this sort of technique and ways that you can safely touch threadsafe objects in the Thread Manager documentation.

Waiting for a background task to complete

In certain situations the problem is not how to do something in the background but how to wait for it to complete without leaving the function currently executing. As an example in an event listener or observer you may want to perform a web request to get some data before you know how to process the event, and you might need this before any other event listeners can run. A synchronous web request is one way, but that would block the UI while it gets the result. Instead you can perform an asynchronous request and then run an event loop to wait for the results

 var thread = Components.classes["@mozilla.org/thread-manager;1"]
                        .getService(Components.interfaces.nsIThreadManager)
                        .currentThread;
 while (!complete)
   thread.processNextEvent(true);

This code will continue running until complete is set to true. While it is running however it will continue to allow UI and other events to be processed leaving the application responsive and allowing any background operations such as web requests to complete and fire their event listeners.

Page last modified 04:25, 31 Aug 2008 by MarkFinkle

Files (0)