Invert your JavaScript
Tags: javascript, functional
Published: 2019-10-27 23:13:42

---
title: Invert your JavaScript
published: true
description: How to use of Inversion of Control in JavaScript to gain flexibility and ease of use
tags: javascript, functional
cover_image: https://thepracticaldev.s3.amazonaws.com/i/7f5thb1p3vvgn6uxmc8t.jpg
---

The outstanding article [Destroy All Ifs] (http://degoes.net/articles/destroy-all-ifs) does an excellent job describing the mechanism of Inversion of Control in Haskell. Unfortunately while Haskell is a beautiful language, it can often be off-putting for people who want to get things done without all the academic mumbo-jumbo. Since I would also like to get things done I think it’s worth exploring the concept of Inversion of Control in standard JavaScript. 

# What is Inversion of Control

[Inversion of Control] (https://martinfowler.com/bliki/InversionOfControl.html) is the method by which frameworks are built. It is a mechanism for injecting new behaviors into an existing system. That sounds pretty abstract, so let’s look at an example.

```javascript
const getWheatBread = (numSlices) => Array(numSlices).fill("wheat");
const getWhiteBread = (numSlices) => Array(numSlices).fill("white");

const makeToast = (isWheat, hasButter, hasJam) => {
  var bread = isWheat ? getWheatBread(1) : getWhiteBread(1);
  bread = bread.map((slice) => slice + " toasted")
  if(hasButter){
    bread = bread.map((slice) => slice + " butter")
  }
  if(hasJam){
    bread = bread.map((slice) => slice + " jam")
  }
  return bread;
};

makeToast(true, true, true)
```

Here we have defined a protocol for making toast. The protocol is
1. Get the bread 
2. Toast it 
3. Maybe add butter
4. Maybe add jam

There is some trouble here. First, what the heck is `makeToast(true, true, true)`? This is very difficult to read, and very easy to get wrong. Second, it’s not very extensible at all. What if we want to specify raspberry jam, or strawberry? We could add more booleans, but that seems like it would get quickly out of hand. Let’s try out this Inversion of Control thing that everyone is so hot about.

## Attempt #1

```javascript
const makeToast = (isWheat, hasButter, applyJam) => {
  var bread = isWheat ? getWheatBread(1) : getWhiteBread(1);
  bread = bread.map((slice) => slice + " toasted");
  if(hasButter){
    bread = bread.map((slice) => slice + " butter");
  }
  bread = bread.map(applyJam);
  return bread;
};

makeToast(true, true, (slice) => slice + " raspberry jam");
```

Nice! We've made the application of jam dynamic, so we can add any kind of jam we want. But what if we want to toast up some rye bread, or try out a new buttering technique? Let’s take it a step further and invert the rest of the steps as well.

## Attempt #2

```javascript
const getWheatBread = (numSlices) => () => Array(numSlices).fill("wheat");
const getRyeBread = (numSlices) => () => Array(numSlices).fill("rye");

const makeToast = (getBread, applyButter, applyJam) => {
  var bread = getBread();
  bread = bread.map((slice) => slice + " toasted");
  bread = bread.map(applyButter)
  bread = bread.map(applyJam)
  return bread;
};

makeToast(
  getRyeBread(1),
  (slice) => {
    busyWait(5); // multiply numbers for 5 minutes so the computer will heat up and soften the butter
    return slice + " butter";
  },
  (slice) => slice + " raspberry jam")
```

Ok great, now we can pass in different behaviors! We've decided that toasting will always work the same way, so we haven't inverted control of it. This is now much more extensible, and it’s much easier to understand what the parameters do. Let’s clean this up a little more.

## Attempt #3

```javascript
const makeToast = (getBread, applyButter, applyJam) =>
  getBread()
  .map((slice) => slice + " toasted")
  .map(applyButter)
  .map(applyJam)
```

Neat. There is a clear separation between things that can change behavior and things that can't. Let’s take another look at the protocol we defined at the beginning:

1. Get the bread
2. Toast it 
3. Maybe add butter
4. Maybe add jam

Our structure is still in place, but each piece can be customized to how we need it.

## Testing

One last thing. Getting our bread might require that we go out to the BreadService. That’s going to be slow, and jeeze who wants to stand up a BreadService just to be able to test our toasting function? What if instead, we injected the `getFakeBread` function when we're running our tests?

```javascript
const getFakeBread = (numSlices) => () => ["fake"];

it('should make some toast', async function() {
  expect(makeToast(
    getFakeBread(),
    doButter, 
    doStrawberry)
  ).to.eql(["fake toasted butter strawberry jam"]);
})
```

Awesome. Let's take stock of what we've gained.

1. We have defined which things in our protocol can change, and which things can't
2. We can inject any behavior we want into the protocol, as long as the function conforms to the expected signature
3. We can easily test our protocol
4. Our code is much easier to understand and get right, since we are explicit about what behaviors we want

Victory!