Background Sync

Rethinking Offline First sync for Service Workers

On the surface, Service Workers look quite similar to Web Workers. They both run on separate threads from the main UI thread, they have a global self object, and they tend to support the same APIs. However, while Web Workers offer a large degree of control over their lifecycle (you can create and terminate them at will) and are able to execute long-running JavaScript tasks (in fact, they’re designed for this), Service Workers explicitly don’t allow either of these things. In fact, a Service Worker is best thought of as “fire-and-forget” — it responds to events in an ephemeral way, and the browser is free to terminate any Service Worker that takes too long to fulfill a request or makes too much use of shared resources.

This led us to our first real hurdle with Service Worker. Our goal, as we originally conceived it, was to use PouchDB’s existing replication APIs to enable bi-directional sync between the client and the server, with the client code isolated entirely to the Service Worker.

[…]

This resulted in a silent error, which took quite a while to debug. The culprit? Well, PouchDB’s “live” sync depends on HTTP longpolling — in other words, it maintains an ongoing HTTP connection with the CouchDB server, which is used to send real-time updates from the server to the client. As it turns out, this is a big no-no in Service Worker Land, and the browser will unceremoniously drop your Service Worker if it tries to maintain any ongoing HTTP connections. The same applies to Web Sockets, Server-Sent Events, WebRTC, and any other network APIs where you may be tempted to keep a constant connection with your server.

What we realized is that “the Zen of Service Worker” is all about embracing events. The Service Worker receives events, it responds to events, and it (ideally) does so in a timely manner — if not, the browser may preemptively terminate it. And this is actually a good design decision in the spec, since it prevents malicious websites from installing rogue Service Workers that abuse the user’s battery, memory, or CPU.

Send messages when you're back online with Service Workers and Background Sync

This example of using background sync looks like it’s specific to Twilio, but the breakdown of steps is broad enough to apply to many situations:

On the page we need to:

  1. Register a Service Worker
  2. Intercept the “submit” event for our message form
  3. Place the message details into IndexedDB, an in browser database
  4. Register the Service Worker to receive a “sync” event

Then, in the Service Worker we need to:

  1. Listen for sync events
  2. When a sync event is received, retrieve the messages from IndexedDB
  3. For each message, send a request to our server to send the message
  4. If the message is sent successfully, then remove the message from IndexedDB

And that’s it.