Using workers in extensions

This article shows you how to use worker threads in extensions to perform tasks in the background without blocking the user interface.

If you haven't already created an extension, or would like to refresh your memory, take a look at the previous articles in this series:

Download the sample

You may download the complete example:

How this differs from previous versions

This version of the stock ticker extension moves the XMLHttpRequest call that fetches updated stock information into a worker thread, which then passes that information back to the main body of the extension's code to update the display in the status bar. This demonstrates not only how to use workers in an extension, but also how to perform XMLHttpRequest in a worker, and how workers and main thread code can pass data back and forth.

The worker

The worker thread's job in this example is to issue the XMLHttpRequest calls that fetch the updated stock information. That's all it does. Every 10 minutes, it calls XMLHttpRequest, and, when the results are received, sends an event back to the main thread with the received data.

So we need to move the refreshInformation() method from the stockwatcher2.js file into a separate file that will host the worker thread. That file, ticker_worker.js, is shown here:

var symbol = "";
function refreshInformation() {
  if (!symbol) {
    throw "No symbol set!";
  }
  var fullUrl =
    "http://quote.yahoo.com/d/quotes.csv?f=sl1d1t1c1ohgv&e=.csv&s=" + symbol;
  function infoReceived()
  {
    var output = httpRequest.responseText;
    if (output) {
      postMessage(output.trim());
    }
    httpRequest = null;
  }
  var httpRequest = new XMLHttpRequest();
  httpRequest.open("GET", fullUrl, true);
  httpRequest.onload = infoReceived;
  httpRequest.send(null);
}
setInterval(function() {
  refreshInformation();
}, 10*60*1000);
onmessage = function(event) {
  if (event.data) {
    symbol = event.data.toUpperCase();
  }
  refreshInformation();
}

When the worker thread is started, the main body of this code (in lines 26-35) is executed. It starts by setting up an interval handler (in lines 26-28) that calls refreshInformation() every 10 minutes.

Then it sets the worker's onmessage event handler to a function which looks at the event passed into it, and does one of two things:

  • If there is a data field on the event, the stock symbol being tracked is set to the upper case version of that value. This is used to initialize the worker, and to change which stock is being monitored.
  • If there is no data field, the refreshInformation() method is called to fetch the latest information about the stock and pass it back to the main thread. This provides a way for the main thread to specifically request that the worker update the stock information at once.

The refreshInformation() method is fairly similar to the previous versions, with two notable exceptions:

  • If the ticker symbol has not been set yet, an exception is thrown (lines 4-6).
  • When the result is received from XMLHttpRequest, instead of immediately updating the displayed information in the status bar, a message is sent to the main thread using the worker's postMessage() method. This is because only the main thread is allowed to access the user interface.

The main thread

The changes here are also relatively minor, but crucial.

The startup() method

This method is called when the extension is first loaded, and needs to be updated to start up and configure the worker. Let's take a look:

  startup: function()
  {
    // Register to receive notifications of preference changes
    this.prefs = Components.classes["@mozilla.org/preferences-service;1"]
        .getService(Components.interfaces.nsIPrefService)
        .getBranch("stockwatcher2.");
    this.prefs.QueryInterface(Components.interfaces.nsIPrefBranch2);
    this.prefs.addObserver("", this, false);
    this.tickerSymbol = this.prefs.getCharPref("symbol").toUpperCase();
    this.worker = new Worker("chrome://stockwatcher2/content/ticker_worker.js");
    // Small little dance to get 'this' to refer to StockWatcher, not the
    // worker, when a message is received.
    var self = this;
    this.worker.onmessage = function(event) {
      self.onworkermessage.call(self, event);
    };
    this.worker.postMessage(this.tickerSymbol);
  },

The worker is set up and configured here in lines 13-22:

  • Line 13 instantiates a new worker, specifying the URI of the ticker_worker.js file.
  • Lines 17-20 change the definition of the worker's onmessage handler so that when the worker calls back to the main thread, the main thread's value of this is correctly the main thread's object instead of the worker's.
  • Line 22 sends a message to the ticker thread to tell it what symbol to monitor.

The observe() method

This method's job is to update which stock is being monitored when the user changes the preferences. It needs to be updated to post a message to the worker to tell it which stock symbol to track.

  observe: function(subject, topic, data)
  {
    if (topic != "nsPref:changed") {
      return;
    }
    switch(data) {
      case "symbol":
        this.tickerSymbol = this.prefs.getCharPref("symbol").toUpperCase();
        this.worker.postMessage(this.tickerSymbol);
        break;
    }
  },

The key here is line 10, which sends the new ticker symbol to monitor to the ticker thread by calling its postMessage() method. This results in the ticker's onmessage handler being called.

The watchStock() and refreshInformation() methods

These two methods are very simple. watchStock() is updated to pass the symbol to the ticker thread, and refreshInformation(), whose main functionality is now in the worker, is updated to simply pass an empty message to the worker, which tells the worker to refresh the stock information immediately.

  watchStock: function(newSymbol)
  {
    this.tickerSymbol = newSymbol.toUpperCase();
    this.prefs.setCharPref("symbol", newSymbol);
    this.worker.postMessage(this.tickerSymbol);
  },
  refreshInformation: function()
  {
    // Empty message just means 'refresh'.
    this.worker.postMessage("");
  },

The onworkermessage() method

This method is called when the worker posts a message back to the main thread. Its job is to update the ticker information that's currently displayed in the status bar, as well as to update the tooltip that appears while the mouse cursor is hovering over the ticker. This code is essentially identical to what's done in the previous version, except that it's done in response to an event instead of within the refreshInformation() method.

A note about ChromeWorkers

Requires Gecko 2.0(Firefox 4 / Thunderbird 3.3 / SeaMonkey 2.1)

Gecko 2.0 added the new ChromeWorker object, which provides a special chrome-only worker that can be used by applications and extensions. This worker object has access to js-ctypes, which standard workers do not have.

See also

Document Tags and Contributors

 Contributors to this page: wbamberg, libri-nozze, trevorh, Sheppy, Jürgen Jeka
 Last updated by: libri-nozze,