Debugging Web Workers in IE10

With
Web Workers, Web applications can be more responsive by offloading long-running, complex JavaScript algorithms to run
in the background. IE10 includes a complete and predictable debugging experience for JavaScript executing within both the
Web page and in Web workers.

In IE10, we’ve enhanced the F12 tools to be aware of the new script contexts created for every Web worker. When you start
debugging (by clicking the Start debugging button within the F12 tools), you’re attaching to the Web page’s script
context as well as the context of any currently running (or future) worker. This is what we mean by complete and predictable
script debugging: all of the script debugging and profiling features that are available in F12 for traditional single-threaded
script behave the same for workers.

The alternative is supporting limited debugging via emulation. Currently, many developer tools don’t support Web Worker debugging
at all. Others only offer it in a simulated environment, for example, using iframes to emulate workers. Unfortunately, the
simulations could mislead developers because the debugger allows invalid scenarios (e.g. DOM access) that would fail during
normal execution to succeed while debugging.

A debugger that offers an unfaithful representation of runtime behavior often results in confusing “heisenbugs,” especially
when migrating existing code over to use workers. In addition, the simulated environments run in the UI’s thread context,
which puts your application at risk of becoming unresponsive while debugging. Both of these potential gotchas make it valuable
for developers to have true debugging support for applications that make use of Web Workers.

Building on what you already know

Since F12 provides the same support for Web Workers as it does for regular scripts, you already know most of how to debug
them. You can set breakpoints within a worker’s executing script, view any local variables, set watches, interact with the
worker via the console, and step through code. This allows developers who are familiar with script debugging to be immediately
productive with Web Workers today. There are, however, a few points of interest—with regards to worker debugging—that are
worth calling out (all screenshots below are using the
Web Worker Harness for test262 demo app).

When your script creates a new Worker object, and the worker’s script
context has been initialized, the script file it is executing will be displayed in the list of script resources. From here,
you’ll be able to select the file and begin debugging as usual.

Screen shot showing F12 developer tools’ Script tab and available scripts menu
Screen shot showing F12 developer tools’ Script tab and available scripts menu

When you’re sitting at a breakpoint within a Web Worker’s script, and you inspect any of its variables or scope, you’ll notice
one key difference between it and regular scripts: it will have a global object of type
WorkerGlobalScope as opposed to the traditional Window object. This is a new object that constrains the Web
worker from accessing any shared memory (like the DOM) with the UI thread that isn’t allowed during normal execution. To
illustrate this, simply add a watch for self.

Screen shot showing F12 developer tools’ Watch tab
Screen shot showing F12 developer tools’ Watch tab

Debugging multiple script contexts

Because the F12 tools are aware of every currently running script context, it can ask all other script engines to pause while
you’re sitting at a breakpoint. This creates a predictable debugging experience because it prevents other script contexts
from interacting with the one you’re currently debugging.

Furthermore, you’re also not confined to debugging one script context at a time. Continuing execution from a breakpoint (either
within a worker or the UI thread) will resume all script contexts, allowing them to run until another breakpoint is hit
(or a debugger statement or exception is encountered).
This allows you to automatically switch “focus” from a worker’s script context to the UI thread’s context, making it trivial
to debug the interaction between them.

While sitting at a breakpoint within a worker-backed application, the Call stack pane will let you see (in addition
to the actual call stack) how many worker instances are running. Each “Root” represents a different JavaScript execution
context. Root #0 represents the main JavaScript UI thread that runs a Web page’s normal script. Root #1 represents
a currently running worker (identifiable by the script file). If there were other workers executing, you’d see them listed
as Root #2, Root #3, etc.

Screen shot showing F12 developer tools’ Call Stack tab
Screen shot showing F12 developer tools’ Call Stack tab

Profiling worker activity and network requests

In addition to debugging, you can also use the profiler to determine the performance of a running worker as you would any
other script. Select the Profiler tab in the F12 tools, click Start profiling, and run the script you want
to profile. Once done, click Stop profiling in the F12 tools. Here’s a view after selecting Call tree from
the Current view drop-down.

Screen shot showing F12 developer tools’ Profiler tab
Screen shot showing F12 developer tools’ Profiler tab

Notice that the URL column indicates which script the individual activity came from. For instance, with this, we can
spot that less time was spent executing the tests inside the worker (execute function inside worker) as compared
to the time spent executing these tests on the UI thread (time taken by run function in the UI thread).

Workers also contribute to the overall network activity, since the browser needs to download the script the worker executes,
as well as any child scripts they depend on (downloaded via
importScripts). All you need to do is select the Network tab and click the Start capturing button.
Here’s a screenshot of a capture that included a worker-initiated script request.

Screen shot showing F12 developer tools’ Network tab
Screen shot showing F12 developer tools’ Network tab

Notice that the download request for the worker.js file shows up, but more interestingly, it has its Initiator
column indicating why it was downloaded: because of a Web worker. Additionally, the 15.2.js file, which was imported
by the worker, also shows up, and indicates that it was initiated by means of a call to the importScripts method.

We welcome your feedback! If you find any issues using Web Workers or its tooling functionality in IE10, please let us know
via Connect.

—Jonathan Carter and Gaurav Seth, Program Managers, Browser Programmability & Tools


IEBlog