Web Workers in Action: Building the Fibonacci Demo and Debugging Like a Pro

March 5, 20264 min read
service workerPWAofflinecachingweb workersprogressive web appsbackground sync

Receiving Data: Building the Fibonacci Demo

Now that communication is wired up, it's time to make the worker actually do something recursively generate Fibonacci numbers, send each result to the page, and render them in real time.


Worker Side: worker.js

The worker doesn't start automatically on load. It waits for a message from the page as a "go" signal:

function getNextFib(curFib) { let fibNum = fib(curFib); // expensive recursive calculation self.postMessage({ idx: curFib, fib: fibNum }); getNextFib(curFib + 1); // immediately start the next one } self.addEventListener('message', function() { getNextFib(0); // kick off from Fibonacci(0) });

Key points:

  1. The message content doesn't matter — whatever the page sends, the worker uses it as a "go" signal only.
  2. It's a tight loopgetNextFib calls itself immediately after posting each result. No yielding, no gaps. This means no incoming messages can interrupt it mid-computation.
  3. The payload shape — each message back has two properties: idx (which Fibonacci index) and fib (the computed value).

Page Side: home.js

Starting the worker:

function startFibs() { worker = new Worker('/js/worker.js'); worker.addEventListener('message', onMessage); worker.postMessage({ start: true }); // content doesn't matter }

Stopping the worker:

function stopFibs() { worker.terminate(); }

Receiving and rendering:

function onMessage(evt) { renderFib(evt.data.idx, evt.data.fib); }

Pull .idx and .fib off evt.data and pass them to the render function.


Seeing It Work ✅

Click Start and Fibonacci numbers start appearing. A few things Kyle observed live:

  1. The page stays fully responsive — scroll, highlight, click buttons — the main thread is completely free while the worker churns in the background.
  2. It slows down exponentially — binary recursion means each step takes roughly twice as long as the previous. Starts feeling sluggish around Fibonacci(42–43). Kyle let it run overnight and never saw it get past Fibonacci(46).
  3. Stop and restart is cleanworker.terminate() kills it entirely. Clicking Start again spins up a fresh worker from Fibonacci(0).

Because the worker is in a tight loop, it can't receive messages while computing. Terminating from outside is the only reliable way to stop it.


The Full Data Flow 🔄

Page Worker ---- ------ new Worker('/js/worker.js') → boots up worker.postMessage({start}) → onMessage fires → getNextFib(0) ← self.postMessage({idx: 0, fib: 0}) onMessage → renderFib(0, 0) ← self.postMessage({idx: 1, fib: 1}) onMessage → renderFib(1, 1) ... continues until worker.terminate() is called

Debugging Web Workers in DevTools 🐛

Yes, you can debug workers just like regular JavaScript.

Console tab:

  • console.log inside a worker shows up normally in DevTools
  • Once a worker is running, you can filter console messages by source to see only worker output

Sources tab:

  • The running worker appears as a listed source
  • Open it, set breakpoints, add watch expressions, pause on exceptions all standard debugging tools work

Kyle hit a minor Chrome rendering glitch live where the worker source wasn't showing immediately in the Sources tab likely a browser bug at that version, not something you did wrong.


Browser Choice for This Workshop

Stick with Chrome (latest). The code is written to be browser-agnostic, but:

  • Firefox — things may run in a slightly different order, not broken but different
  • IE11 — completely broken (tested live, surprised even Kyle)

Verify other browsers after the workshop if needed.


Web Workers: Full Section Recap 🎯

  1. What web workers are — offloading CPU-heavy work off the main thread to keep the page responsive
  2. Three worker types — dedicated (most useful), shared (progressive enhancement only), child (niche)
  3. Spinning one upnew Worker('/js/worker.js') is literally all it takes
  4. Two-way messagingpostMessage + evt.data on both sides, symmetrically
  5. Structured Clone Algorithm — data is always copied, not referenced; functions can't be cloned
  6. Data transfer history — transferables → shared array buffers → Spectre → back to copying
  7. The Fibonacci demo — tight loop, terminate to stop, fully responsive page throughout
  8. Debugging — full DevTools support including breakpoints and watch expressions

Service workers are next — and with this foundation solid, their unique powers (surviving beyond the tab, living as a background zombie) will make a lot more sense.