The Async Series: Promises
Tags: codenewbie, beginners, javascript, webdev
Published: 2021-08-11 02:38:50

# Async Simplified, I promise

Callbacks can help with managing the order our async calls. However, things get messy if you have too many. Luckily there's an alternative that definitively shows... some promise.

# The Gates of Hell

In the last post of this series, we arrived at the following solution using nested callbacks.

```javascript
//replace reference to doPrintGreenRed with an anonymous function
printBlue("Blue", function(){
   //calls printGreen with our desired parameter 
   printGreen("Green", function(){
     //calls print red with our desired parameter
     printRed("Red");
   });
});
```

However, the more calls we need to make to more callbacks we need to define. At some point you will experience a phenomena called **callback hell**.

![image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ks661p211fmaivpzp42b.png)

Not to mention how messy it will get to perform [exception handling](https://en.wikipedia.org/wiki/Exception_handling) in each callback.


```javascript
try{
 printBlue("Blue", function(){
   try{
     printGreen("Green", function(){
       try{
         printRed("Red");
       }catch(e){
         console.error(e);
       }
     });
   }catch(e){
     console.error(e);
   }
 });
}catch(e){
   console.error(e);
}
```

# So what now?

In the 6th version of JavaScript released in 2015, promises were released. Instead of accepting callbacks directly, async functions can now return Promise objects. 

These promises objects provide then() method that will take the callback and execute it when the main work of the async function is completed.

Luckily, our print functions return promises so our nested callbacks can be rewritten as.

```javascript
 printBlue("Blue")//moved our callback from here
  .then(function(){//to here
    printGreen("Green")
     .then(function(){
       printRed("Red");
     })  
  })
```

We got the desired output. However, is this really an improvement over the callback approach? It still looks very similar. Well the thing about then() is that it returns another promise! 

then() returns another promise after the previous one is said to have been **resolved**. 

You can call then() repeatedly to form a what is called a **promise chain**.

```javascript
 printBlue("Blue")
  .then(function(){
    //only executes after printBlue() resolves
    printGreen("Green");// instead of calling then here
  })
  .then(function(){ // we call it here
    printRed("Red");//only executes after printGreen resolves
  })
  .catch(e){
    console.error(e);
  }  
```

Now, the nesting has been flattened, but the main advantage here is the use of the catch() method that is also provided by the promise object.

The catch of the end of the chain will handle any errors that may have been thrown at any part of the chain! 

This is a great improvement in terms of readability and error handling.

# Making Promises

Just like how we are able to write a higer-order add() we can also write a version of that function that returns a promise. Unlike the printRed/Green/Blue functions, the promise returned by add() will **resolve with a value**. That value will be received by any function passed to the then() method.


```javascript
function add(a, b){
  //create a promise object
  const promise = new Promise(function(resolve, reject){
    if(typeof a !== "number" or typeof b !== "number")
      reject("Invalid parameter error");//how errors are thrown
    else
      resolve(a + b);//how values are returned when the work of the function is complete
   })
  return promise;//return our promise
}
```
When creating a promise object you need to supply it with 2 callbacks; resolve() and reject(). 

Instead of using **return** to return a value we use the resolve() function. What ever is passed to resolve() will be passed to any callback given to then().
 
Instead of using **throw** to throw an error we use the reject() function. What ever is passed to reject() will be passed to any callback given to catch().

```javascript
add(5,10)
  .then(function(ans){
    console.log(ans);//logs 15
    return ans;//passes this value to next then in the chain
  })
  .then(function(ans){
    return add(ans, 5);//returns a new promise to the next then
  })
  .then(function(ans){
    console.log(finalAns);//logs 20
  });

add(11, 'cat')
  .then(function(ans){
    console.log(ans);
    //this is not executed because of the type check in the add()
  })
  .catch(function(error){
   console.error(error);//logs 'Invalid parameter error'
  });
```

Any value returned in the callback passed to then() will be passed forward to the callback of the next then() in the chain. That's how the 2nd then() callback was able to receive the result of the 1st then() callback.

# Promise.all()

In the examples we have looked at so far, the **ordering** of our async calls was important so we used **then** to perform **flow control**. In cases where our async calls are **independent** but we need to combine their result of each call in some say, we can use Promise.all().

Promise.all() will execute multiple promises asynchronously but collect their final values in an array.

```javascript

let promise1 = add(5, 10);
let promise2 = add(11, 12);
let promise3 = add(7, 8);

//Async execution (faster)
Promise.all([promise1, promise2, promise3])
  .then(function(result){
    console.log(result);// logs [15, 23, 15]
  })
```
Because our additions are independent of each other, we do not use then() to perform the additions synchronously. Instead, they are kept async. This would actually execute faster than synchronizing them. 

**Important**: We only synchronize our calls with then() if the **order matters** or the calls are **dependent** on each other.

```javascript
//Sync execution (slower), not needed in this case 
//also relies on global state arr

let arr = [];

add(10, 5)
  .then(function(sum1){
    arr.push(sum1);
    return add(11, 12);
  })
  .then(function(sum2){
    arr.push(sum2);
    return add(3, 4)
  })
  .then(function(sum3){
    arr.push(sum3);
    console.log(arr);
    //logs [15, 23 7] the result of all promises that resolved in the chain
    //this result is only available in this scope
  });

console.log(arr);
//logs [] empty array because this log runs asynchronously with the first call to add(). 
//The other promises have not resolved yet.

```

# Conclusion

In this post we showed how Promises improve over nested callbacks by chaining them together. However, the limitation is that the result of all the calls are only available at the end of the chain. 

As always you can try any of the code in this article at this [REPL](https://replit.com/@Snickdx/async-2#index.js).

Is there anyway we can improve on this? If you stick around I promise I will tell in the final post of this series.