<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://your-docusaurus-site.example.com/</id>
    <title>A Million and One Moos Blog</title>
    <updated>2026-05-10T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://your-docusaurus-site.example.com/"/>
    <subtitle>A Million and One Moos Blog</subtitle>
    <icon>https://your-docusaurus-site.example.com/cow.svg</icon>
    <entry>
        <title type="html"><![CDATA[A Diservice to Service Workers]]></title>
        <id>https://your-docusaurus-site.example.com/a-diservice-to-service-workers</id>
        <link href="https://your-docusaurus-site.example.com/a-diservice-to-service-workers"/>
        <updated>2026-05-10T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[وقل ربي زدني علما]]></summary>
        <content type="html"><![CDATA[<p>وقل ربي زدني علما</p>
<p>'and say: O lord increase me in knowledge’ <a href="https://quran.com/20/114" target="_blank" rel="noopener noreferrer" class="">(20:114)</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="post-mortem-using-service-workers-for-offline-first-web-apps">Post-Mortem: Using Service Workers for Offline-first Web Apps<a href="https://your-docusaurus-site.example.com/a-diservice-to-service-workers#post-mortem-using-service-workers-for-offline-first-web-apps" class="hash-link" aria-label="Direct link to Post-Mortem: Using Service Workers for Offline-first Web Apps" title="Direct link to Post-Mortem: Using Service Workers for Offline-first Web Apps" translate="no">​</a></h2>
<p>بسم الله الرحمن الرحيم</p>
<p>Not too long ago I was visiting a local mosque (and community center) that I frequent often and noticed that the slideshow display wasn’t working. After some discussion with one of the admins, we jointly proposed a new slideshow display to replace the defunct display they had at the time. The new display would be a web application which achieves feature-parity with the previous display and adds some extra conveniences.</p>
<p>As per the usual, I underestimated the amount of time it takes to create a ‘simple’ slideshow which is resilient and robust. After all, slideshows are expected to run for months (even perhaps years) without stopping or crashing, ideally without any intervention from my side.</p>
<p>The first version of the slideshow was live and deployed to the center a couple of days after the proposal. About 16 hours later I received my first complaint:</p>
<!-- -->
<p>“Hey, it seems the slideshow has stopped working?“</p>
<p>Attached was a photo similar to this:</p>
<p><img decoding="async" loading="lazy" alt="Firefox no connection" src="https://your-docusaurus-site.example.com/assets/images/ff-no-conn-e65dc6f262d0280cc44d875edf34f553.png" width="1920" height="1012" class="img_ev3q"></p>
<p>I was somewhat bewildered, as the (somewhat humble) device running the slideshow has around-the-clock network access via ethernet. After some debugging and testing it appeared that the center’s internet was significantly poorer than expected. Multiple times per day the center’s internet connection would cut for minutes on end despite being on ethernet.</p>
<p>Knowing this, I could have taken the simple route and packaged the app in <a href="https://www.electronjs.org/" target="_blank" rel="noopener noreferrer" class="">Electron</a> and have to deal with this issue. However, this would make updates much more difficult so I decided it wasn’t the right solution.</p>
<p>This is when I was introduced the main villain (and hero?) of the post, the notorious <a href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers" target="_blank" rel="noopener noreferrer" class="">Service Worker</a> (SW). Simply put, a SW is a background thread that intercepts all HTTP requests made by a javascript application. Whenever a request is executed the SW is notified and can then decide what action to perform. It can allow the request to continue as usual, return a cached file stored on the user's file system, transform the http request to fetch a resource from another domain, etc. The possibilities are endless. Here’s an example of a SW:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">addEventListener</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"fetch"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  event</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">respondWith</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">cacheFirst</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">cacheFirst</span><span class="token punctuation" style="color:#393A34">(</span><span class="token parameter">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain">request</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> event</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> cached </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:#00009f">await</span><span class="token plain"> caches</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">match</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">request</span><span class="token punctuation" style="color:#393A34">.</span><span class="token property-access">url</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">cached</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">fetch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">request</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> cached</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>So I decided to use a SW. Whenever the app starts or refreshes itself it would cache all relevant files (html, css, js, images, etc.) and delete any stale files. If the network conditions are poor it would fallback to the older (already cached) version of the app and try again later. This theoretically should’ve prevented any poor network conditions from crashing the app. Or so I thought…</p>
<p>Lo and behold only 48 hours after I deployed the now updated version with a SW I received another message that stated that the slideshow was no longer displaying:</p>
<p><img decoding="async" loading="lazy" alt="Firefox Timeout Screen" src="https://your-docusaurus-site.example.com/assets/images/ff-timeout-b0d6308a563ac8219d654d55f6b8a7f9.jpeg" width="1000" height="750" class="img_ev3q"></p>
<p>This left me more bewildered than the previous time. As I had accounted for when the network conditions were poor and the files should have been served from cache. However upon further investigation it appeared that I forgot to account for one edge case: when the network takes too long to respond and the user-agent (firefox browser in this case) kills the SW. The default timeout for SW as of writing this <a href="https://github.com/w3c/ServiceWorker/issues/1182" target="_blank" rel="noopener noreferrer" class="">seems to be 5 minutes</a> (300 seconds).</p>
<p>The solution? Just add timeouts to SW requests that are shorter than the user-agent’s max timeout. Continuing from the previous example, update the <code>cacheFirst</code> function like so:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// any value less than user-agent timeout</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">TIMEOUT_MILLIS</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">60_000</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword control-flow" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">cached</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword control-flow" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">fetch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">request</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token literal-property property" style="color:#36acaa">signal</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token maybe-class-name">AbortSignal</span><span class="token punctuation" style="color:#393A34">.</span><span class="token method function property-access" style="color:#d73a49">timeout</span><span class="token punctuation" style="color:#393A34">(</span><span class="token constant" style="color:#36acaa">TIMEOUT_MILLIS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>Lesson of the day: always add timeouts to your SW requests, especially if you want to create a true offline-first web app that is resilent to poor network conditions. Also, eat your vegetables. See ya next time!</p>
<p><img decoding="async" loading="lazy" alt="Cat Veggies" src="https://your-docusaurus-site.example.com/assets/images/cat-veggies-64bc3e086171353e4f8e70668eeaedd1.gif" width="362" height="480" class="img_ev3q"></p>]]></content>
        <author>
            <name>Mostafa Elbannan</name>
        </author>
        <category label="javascript" term="javascript"/>
        <category label="frontend" term="frontend"/>
        <category label="progressive web app" term="progressive web app"/>
    </entry>
</feed>