Snippets

Clean Code: JavaScript

This is excellent and you should read it all.

Software engineering principles, from Robert C. Martin’s book Clean Code, adapted for JavaScript. This is not a style guide. It’s a guide to producing readable, reusable, and refactorable software in JavaScript.

Not every principle herein has to be strictly followed, and even fewer will be universally agreed upon. These are guidelines and nothing more, but they are ones codified over many years of collective experience by the authors of Clean Code.

Our craft of software engineering is just a bit over 50 years old, and we are still learning a lot. When software architecture is as old as architecture itself, maybe then we will have harder rules to follow. For now, let these guidelines serve as a touchstone by which to assess the quality of the JavaScript code that you and your team produce.

One more thing: knowing these won’t immediately make you a better software developer, and working with them for many years doesn’t mean you won’t make mistakes. Every piece of code starts as a first draft, like wet clay getting shaped into its final form. Finally, we chisel away the imperfections when we review it with our peers. Don’t beat yourself up for first drafts that need improvement. Beat up the code instead!

Async events in ServiceWorkers with "event.waitUntil"

It’s asynchronous, see? So even though all that network code appears before the return statement, it’s pretty much guaranteed to complete after the cache response has been returned. You can verify this by putting in some console.log statements:

Code language: JavaScript

caches.match(request)
.then( responseFromCache => {
  if (responseFromCache) {
      event.waitUntil(
          fetch(request)
          .then( responseFromFetch => {
              console.log('Got a response from the network.');
              caches.open(staticCacheName)
              .then( cache => {
                  cache.put(request, responseFromFetch);
              });
          })
      );
      console.log('Got a response from the cache.');
      return responseFromCache;
  }

Those log statements will appear in this order:

Got a response from the cache.
Got a response from the network.

That’s the opposite order in which they appear in the code. Everything inside the event.waitUntil part is asynchronous.

Here’s the catch: this kind of asynchronous waitUntil hasn’t landed in all the browsers yet. The code I’ve written will fail.

But never fear! Jake has written a polyfill. All I need to do is include that at the start of my serviceworker.js file and I’m good to go:

Code language: JavaScript

// Import Jake's polyfill for async waitUntil
importScripts('/js/async-waituntil.js');

Safari bug with gradients that fade to "transparent"

Say you have a gradient in CSS that goes from red to transparent. Easy, right? Like this:

Code language: CSS

.element {
  background: linear-gradient(
    to bottom,
    red, 
    transparent
  );
}

There is a pretty big gotcha here, though.

In Chrome (also Android), Firefox, and Edge, you’d be all good.

But in Safari (also iOS), you’d not be good.

The same test page as before, viewed in both the iOS simulator version of Safari, and the macOS version of Safari. The two rectangles, each with a gradient background colour are different this time. The red in the rectangle on the left fades from red to black, as it fades to transparent, which appears to be a bug. The rectangle on the right fades to transparent while still maintaining the red colour, as expected.
The element on the left in each browser demonstrates the problem.

The problem, the best I understand it, is that transparent is being interpreted (and interpolated) as “transparent black”.

To fix it, you have to set the color as a fully transparent version of that exact color. Like:

Code language: CSS

.element {
  background: linear-gradient(
    to bottom,
    red,
    rgba(255, 0, 0, 0)
  )
}

[…]

Sass can help out, if you are using that:

Code language: Sass

.element {
  background: linear-gradient(
    to bottom,
    #eb8fa9,
    rgba(#eb8fa9, 0);
  )
}