Returns, callbacks and the whole zoo
Tags: architecture, callback, promise, javascript
Published: 2018-06-05 09:24:23

---
title: Returns, callbacks and the whole zoo
published: true
description: An overview of inter-application communication
tags: architecture, callback, promise, javascript
cover_image: https://thepracticaldev.s3.amazonaws.com/i/09i8gypgrpzvhg8wdbjt.jpg
---

I'm currently co-developing a standardized communication between two entities `A` and `B`. To release my mind from all the thoughts about how an why and balancing benefits and drawbacks of different methods I'd like to share them with you. And maybe your 2¢ will help me optimizing our strategy. I'd like to add that my context is browser based JavaScript but some ideas might be generalizable.

## ➡ calling ##
If `A` wants to call `B` I find the following ways:
1. Calling a predefined function/method in the scope of `A`: `[B.]act(param)`
2. Using a common communication transport:
   1. by message: `transport.postMessage({action: act, parameter: param})` used in inter-frame and main-thread/worker-thread communication but also (miss)usable within one document context (see Appendix A)
   2. by event: `transport.dispatchEvent(new actEvent(param))`.

The second point might seem overelaborate but is quite useful for decoupling and even necessary if both entities are not in the same context. One advantage of the second way is that `A` will just continue to work even if `B` is (temporarily) not available. But on the other hand `B` needs to actively listen to the specified events (a message is received just like any other event).


## ⬅ answering ##
There are more ways for `B` to react and submit a success state or return data from acting.
1. directly return a result in case of ➡1: `function act(param) { …; return success }`
2. alike ➡1: call a predefined function/method in the scope of `B`: `[A.]doneActing(success)`
3. alike ➡2: use a common transport e.g. `transport.dispatchEvent(new doneActingEvent(success)`
4. use a callback contained in `param`: `param.callWhenDone(success)`
5. return a promise, fulfilled or rejected depending on success `return new Promise(function (f, r) { (success ? f : r)(successData) })`

The first is the standard way for all non-asynchronous contexts and again the second might be necessary in some cases. Asynchronous decoupling is achieved by callbacks resp. promises while promises seem to be the new "right" way to do it. 

## conclusion? ##
What are your thoughts, when should one use either one? Is interchangeability shown in Appendix B leading to one way for entity `A` and another for `B`? What about a hierarchy between both entities, would your recommendation change depending on weather `A` or `B` are more important?

# Appendix #

## A: inter-window communication using `postMessage` ##

```javascript
class B {
  constructor (targetWindow) {
    targetWindow.addEventListener('message', message => console.log(`B is reading: '${message.data}'`))
  }
}

class A {
  constructor (targetWindowOfB) {
    this.targetOfB = targetWindowOfB
  }
  letBAct (message) {
    this.targetOfB.postMessage(message, '*')
  }
}

let entityA = new A(window)
let entityB = new B(window)
entityA.letBAct('Hy, here is A, are you listening?')
```

> B is reading: 'Hy, here is A, are you listening?'


## B: transformation ##
Finally the trivial, *most* methods are interchangeable (1 is left out as a target). Here interchangeable `n` ➝ `m` is defined as the answering entity using method `n` and the receiving entity using method `m`.

1 ➝ 2: 

```javascript
doneActing(act(param))
```
    
1 ➝ 3: 

```javascript
transport.dispatchEvent(new doneActingEvent(act(param)))
```

1 ➝ 4: 
  
```javascript
param.callWhenDone(act(param))
```

1 ➝ 5:

```javascript
var returnPromise = new Promise(function (f, r) {
  let success = act(param)
  (success ? f : r)(success)
  /* or */
  f(act(param))
})
```

---    

2 ➝ 3: 

```javascript
function doneActing (success) {
  transport.dispatchEvent(new doneActingEvent(success))
}
```
    
2 ➝ 4: 
  
```javascript
function doneActing(success) {
    param.callWhenDone(success)
}
```

2 ➝ 5:

```javascript
let returnPromise = new Promise(function (f, r) {
  function doneActing(success) {
    (success ? f : r)(success)
  }
})
```
  
---    

3 ➝ 2:

```javascript
transport.addEventListener('doneActingEvent', event => doneActing(event.data))
```
  
3 ➝ 4:

```javascript
transport.addEventListener('doneActingEvent', event => param.callWhenDone(event.data))
```
  
3 ➝ 5:

```javascript
let returnPromise = new Promise(function (f, r) {
  transport.addEventListener('doneActingEvent', event => (event.data ? f : r)(event.data))
})
```
  
---
  
4 ➝ 2:
  
```javascript
param.callWhenDone = doneActing
```
  
4 ➝ 3:

```javascript
param.callWhenDone = success => transport.dispatchEvent(new doneActingEvent(success))
```
  
4 ➝ 5:

```javascript
let returnPromise = new Promise(function (f, r) {
  param.callWhenDone = success => (success ? f : r)(success)
})
```

---
  
5 ➝ 2:
  
```javascript
promiseResponse.finally(doneActing)
```
  
5 ➝ 3:

```javascript
promiseResponse.finally(param.callWhenDone)
```
  
5 ➝ 4:

```javascript
promiseResponse.finally(success => transport.dispatchEvent(new doneActingEvent(success))
```