Yet another intro to Promise, async - await in JavaScript
Tags: javascript, webdev, tutorial
Published: 2020-07-03 17:52:59

###Heads Up
* I am assuming you know basic JavaScript (>= ES6)
* This post is rather semantic than syntactic
* I have put up links for further explanations wherever I thought 
  would be necessary.

##Let's Go
> Examples are the best way to make someone understand something because it's easier to correlate with something practical than digest theory - *Asif Mohammad*

For example, every time we search for the meaning of a word online we tend to read its examples to make more sense.

See what I did there? :wink:

So lets just consider an example of baking and eating some delicious cake. We can break down the whole process into three basic steps
1. Baking Cake
2. Serving Cake
3. Eating Cake

The Javascript equivalent of this process could be portrayed as the following functions

```javascript
const bakeCake = () => console.log('Cake is baked');
const serveCake = () => console.log('Cake is served');
const eatCake = () => console.log('Cake eaten');
```

Yes, it is neither a proper equivalence nor I am eligible to be a good cook but it serves the purpose if not the cake.:wink:

Our cake baking journey would go something like

```javascript
bakeCake(); // Cake is baked
serveCake(); // Cake is served
eatCake(); // Cake is eaten
```

But most real world scenarios like baking cake and scenarios on the web like fetching user posts, have something in common, **they take time**

Lets adjust our functions so that they reflect such and lets consider each of our step takes 2 seconds of time
```javascript
const bakeCake = () => {
  setTimeout(()=>{
    console.log('Cake is baked')
  }, 2000);
};
const serveCake = () => {
  setTimeout(()=>{
    console.log('Cake is served')
  }, 2000);
};
const eatCake = () => {
  setTimeout(()=>{
    console.log('Cake is eaten')
  }, 2000);
};
```
We cannot call these three functions sequentially because they will not run synchronously. [Why?](http://latentflip.com/loupe)

So we should follow the standard **callback** pattern which is being used for a long time now.
##Using Callback Functions
```javascript
const bakeCake = (cbkFn) => {
  setTimeout(()=>{
    console.log('Cake is baked');
    cbkFn();
  }, 2000);
};

const serveCake = (cbkFn) => {
  setTimeout(()=>{
    console.log('Cake is served');
    cbkFn();
  }, 2000);
};

const eatCake = () => {
  setTimeout(()=>{
    console.log('Cake is eaten')
  }, 2000);
};

bakeCake(()=>{
  serveCake(()=>{
    eatCake();
  });
});
```

##Understanding the callback pattern
When we use callbacks we expect the function we pass to be called back when required (hence the name *callback* functions). The problem with callbacks is the often occurring [Callback Hell](http://callbackhell.com/).

Consider our cake baking, when the steps are extended it becomes
```js
bakeCake(() => {
  decorateCake(() => {
    tasteCake(() => {
      cutCake(() => {
        serveCake(() => {
          eatCake(() => {

          });
        });
      });
    });
  });
});
```
This is what we call as the Callback Hell. The more things you are willing to do in this process the more complex and messy it will get. It works, It's fine but we always want something ~~batter~~ better.
##Promise
[Promise](https://javascript.info/promise-basics) as the name goes is a pattern, rather than being an object/function, where you are promised the execution of a piece of code and it enables you to code further based on your trust on that promise. JS Engine is a machine so you can always trust when it promises you, unlike us evil Humans.

Rewriting our example using promises.
Lets skip serving the cake (yes we are the wild ones who eat directly off the stove)

```js
const bakeCake = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('Cake is baked');
      resolve();
    }, 2000);
  });
};

const eatCake = () => {
  setTimeout(() => {
    console.log('Cake is eaten');
  }, 2000);
};

bakeCake().then(eatCake);
```
What we did here is instead of executing the `bakeCake` function normally, we are enclosing it in a **Promise**d environment. Previously we did not return anything in `bakeCake` but now we are returning a *Promise* to the callee.
 
A promise that the piece of code enclosed is executed with an assurance that once its completed, either successfully or broke down due to some abnormality, you will be notified.

`resolve` being the indicator of success and
`reject` for any abnormal execution (mostly for an error)

In our case of `bakeCake` we are resolving the promise ( notifying the callee that the piece of code which was promised to be supervised has completed successfully) and on the callee's side we can listen to the notification with `then` and the abnormalities with `catch` which we haven't covered here. 

Promises enable [chaining](https://www.javascripttutorial.net/es6/promise-chaining/) which is not possible by callbacks.
Suppose we had to log our cake baking. We could chain our functions as
```js
const bakeCake = (cakeLog) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('Cake is baked');
      cakeLog.push('Perfectly baked!')
      resolve(cakeLog);
    }, 2000);
  });
};

const serveCake = (cakeLog) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('Cake is served');
      cakeLog.push('Served Well');
      resolve(cakeLog);
    }, 2000);
  });
};

const eatCake = (cakeLog) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('Cake is eaten');
      cakeLog.push('Ate like its the last cake on earth')
      resolve(cakeLog);
    }, 2000);
  });
};

bakeCake([])
  .then(serveCake)
  .then(eatCake)
  .then(console.log);
```
We pass in an empty array `[]` to `bakeCake` and when it resolves it pushes its own log statement into the array then reaches the first `then` when resolved and the `function` you pass as the parameter to `then` gets the parameter as the content you passed into the `resolve` call.

To understand better. We can rewrite the function calls as
```js
let cakeLog = [];
bakeCake(cakeLog).then(cakeLog => {
  serveCake(cakeLog).then(cakeLog => {
    eatCake(cakeLog).then(cakeLog => {
      console.log(cakeLog);
    });
  });
});
```
We pass `cakeLog` into `bakeCake` and we get it back (after getting updated in the cakeLog) as a parameter to the function we pass in to the `then` call. So we can pass it along to `serveCake` and repeat the same till we need to consume the accumulated/gross data.

It makes more sense when we correlate to a actual scenario like
```js
let userID = 1001;
getUser(userID)
    .then((user) => getPosts(user))
    .then((posts) => getTotalLikes(posts))
    .then((likeCount) => console.log(likeCount));
```
**But** We always want better.
##async - await
async - await enable us to write asynchronous code just like how we would write synchronous code by acting as a syntactical sugar to the powerful Promise pattern.

A blueprint of using async await with respect to the underlying Promise pattern would be
```js
async function(){
  let paramYouSendIntoResolve = await promReturningFn(); 
}
```
1. Call your asynchronous function but use an **await** keyword before it
2. Instead of passing in a function to capture the resolved data. Take it as a return value of the function. ***Wow***
3. Just one minor discomfort. As you are doing asynchronous stuff amidst of ever synchronous JS flow. Just append **async** to the function where you use **await** so that JS Engine knows you are going to do async stuff and interprets accordingly because it has to turn them into Promises later.

Back to our cake baking. (excluded the logging stuff) 
```js
const bakeCake = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('Cake is baked');
      resolve();
    }, 2000);
  });
};

const serveCake = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('Cake is served');
      resolve();
    }, 2000);
  });
};

const eatCake = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log('Cake is eaten'); 
      resolve();
    }, 2000);
  });
};

(async ()=>{
  await bakeCake();
  await serveCake();
  await eatCake();
})();
```
Notice that we have used an [IIFE](https://developer.mozilla.org/en-US/docs/Glossary/IIFE) here to force async function execution.

There we are! 
We have reached the ability to call asynchronous functions ~~Asif~~ as if they were synchronous. 

**Thanks for reading. I hope you got something out of this**