Inversion of Inversion of Control
Published: 2021-07-24 15:32:08

> [Inversion of Control](https://martinfowler.com/bliki/InversionOfControl.html) is a common phenomenon that you come across when extending frameworks. Indeed it's often seen as a defining characteristic of a framework. -- Martin Fowler

The key part is the 'framework' calls you rather than you calling the 'framework'. The 'framework' doesn't need to be any specific thing. An operating system, a language runtime, or an application framework could be that 'framework'. As you register an event handler (no matter explicit .addEventHander or implicit lifecycle), you are already using IoC.

But is IoC always good? Or we should say, **is IoC always what we desired?**

IMO most of IoC is not intended (DI is the one I think of so far), but **the control is by nature inverted, especially in human-computer interaction programming**, because this is how the computer works: it is the user controls. If user doesn't input then the program just keeps idling. 

```ruby
# The example provided in the blog above 
puts 'What is your name?'
name = gets
process_name(name)
puts 'What is your quest?'
quest = gets
process_quest(quest)
```

Then here comes a question: why does command line enquiry seem to keep the control? Isn't the thread blocking and waiting for a signal as well? That is true and in fact the computer is in certain degree event-driven, in form of interruptions. However, conceptually it is in control because it's an imperative process: statements executed in order. There is a clear flow of control.

And I think **sometimes we need our control back.** Like when there are callback hells. And luckily coroutine(async/await) is to rescue.

```js
// in control

async function foo() {
  const x = await fetch('http://bar.com/');
  //...
}

// control inverted

function foo() {
  fetch('http://bar.com/').then(x=>{
    //...
  })
  //...
  // we get two flows of control now?
}
```
Maybe this example is so simple that you don't see any difference :/. So let's see another example: you are going to manage a WebSocket connection and it has some rules:
1. when connected, server sends you 'LOGIN' and you should reply 'LOGIN:\<auth token\>' and finally when server replies 'VERIFIED' the connection is successful.
2. whenever server sends 'PING', client must reply 'PONG' (in 30 seconds maybe) otherwise connection will be closed.

Normally WebSocket is event-driven and we might write a version like this
```js
const socket = new WebSocket('foo.bar');
let isVerified = false;
socket.onopen = ()=> {
  socket.onmessage = ({data}) => {
    if(data=='LOGIN') {
      socket.send('LOGIN:'+authToken);
    }
    else if(data=='VERIFIED') {
      isVerified = true;
    }
    else if(data=='PING') {
      socket.send('PONG')
    } else {
      // process the data
    }
  }
}
```
This code might work. But there are edge cases like what if server doesn't reply the expected message? To add more state (isLogined, isSuccessful) is a solution but it's redundant (you might check current state whenever callback executed), and not easily-extendable if there are more steps.

However if the control is inverted, the logic will become much more natural. Imagine we have a `IoCWebSocket` and it provides a modified `WebSocket` with extra methods:
```js
// resolve when it's open
async ready():Promise<void>;

// resolve when there is a incoming message
// reject when websocket error
async read():Promise<string>;
```
Then the logic becomes:
```js
const socket = new IoCWebSocket('foo.bar');
// ...
// assume inside an `async` function body
await socket.ready();
let next = await socket.read();
if(next!='LOGIN') {
  throw Error('Unexpected reply '+next);
}
socket.send('LOGIN:'+authToken);
next = await socket.read();
if(next!='VERIFIED') {
  throw Error('Unexpected reply '+next)
}
while(true) {
  try {
    next = await socket.read();
    if(next=='PING') {
      socket.send('PONG')
    }
    else {
      // process the data
    }
  }
  catch {
    // may catch error and re-throw if it's not due to connection closed.
    break;
  }
}
```

Do you notice the order (which is an implicit rule) is naturally guaranteed?

And we may have many analogous situations like drag-n-drop, step-by-step enquiry, [cheat codes](https://en.wikipedia.org/wiki/Konami_Code), (stateful) animations and interactions(hold to activate/n-times click)......they are procedures with extra (temporary) context informations, they're designed to have an order, they could own their own flow of control.

Bonus: [Co-routines as an alternative to state machines](http://eli.thegreenplace.net/2009/08/29/co-routines-as-an-alternative-to-state-machines/)

This post is about 'what'. As for 'how', I'm still investigating the most optimal solutions. `Promise` and async/await are fine in most cases. Sister post: [Promise!==coroutine](https://dev.to/3shain/promise-coroutine-548i)