Callbacks vs. Promises
Published: 2020-07-27 15:48:43

Basically whenever you need to get data from a server, do something with that data, maybe grab more data from another server you'll be using either **callbacks** or **Promises**.

It's a way to do something you need to do with that data once it comes back.

## Callbacks

Callbacks are the parameter we use in many cases to provide functionality. A very common one is using a callback for `addEventListener()` when in web development. 

```js
document.querySelector('button').addEventListener('click', () => {
  // This is where your logic goes in the callback!
})
```

### The Bad

When we end up with a ton of `if else` statements nested, resulting in _callback hell_, we're again normally fetching data or manipulating data and need to ensure each step of the way an error hasn't occurred.

This example is from [callbackhell.com](http://callbackhell.com/) and shows pretty well this issue. 

This is going through a directory and attempting to resize files. It's not as important to understand the code here but simply the structure.

```js
fs.readdir(source, function (err, files) {
  if (err) {
    console.log('Error finding files: ' + err)
  } else {
    files.forEach(function (filename, fileIndex) {
      console.log(filename)
      gm(source + filename).size(function (err, values) {
        if (err) {
          console.log('Error identifying file size: ' + err)
        } else {
          console.log(filename + ' : ' + values)
          aspect = (values.width / values.height)
          widths.forEach(function (width, widthIndex) {
            height = Math.round(width / aspect)
            console.log('resizing ' + filename + 'to ' + height + 'x' + height)
            this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
              if (err) console.log('Error writing file: ' + err)
            })
          }.bind(this))
        }
      })
    })
  }
})
```

The _Pyramid of Doom_ makes an appearance. On the left we see the indentation gradually getting further to the right and then closing with a bunch of `}` and `})`. Easy recipe to make simple errors based on one parenthesis or curly bracket.

Each step of the way we nest another `if else` and there is a total of **4 callback functions** nested in there.

### The Good

This is the only way as JavaScript developers we were able to do these asynchronous tasks prior to `Promise`s. 

It's a good thing to know of, be able to read and understand what's happening and why. There will be code out there you may work on for a long time most likely that has this structure.

Some people prefer it and that's totally fine! We all have our own preferences as devs and if the task is completed properly there's no problem truly. 

Not to mention the example above isn't written as well as it could be. Again visit [callbackhell.com](http://callbackhell.com/) as it has this example and goes through steps on how to improve it while still using callbacks. 

Steps such as name your functions instead of using anonymous ones, try to keep it shallow when nesting and always handle each potential error.

>  **Remember, only you can prevent callback hell and forest fires**
> > --[callbackhell.com](http://callbackhell.com/)

## Promises

The `Promise` in JavaScript was made official in the release of ES6. Since then they've become a widely supported and accepted feature of JavaScript. 

A `Promise` is essentially an object that represents some asynchronous task that will eventually succeed or fail. If it 'suceeds' we say it is **fulfilled** and we will use the `.then()` method to handle success.

If a `Promise` fails it is said to have been **rejected** and we will use the `.catch()` method to handle that failure and error.

### The Bad

`Promise`s are meant to be a asynchronous tasks. And since JavaScript lets us pretty much willy nilly write whatever we want we can end up with some bad code. 

[Craig Martin](https://dev.to/craigmichaelmartin) here on Dev.to has written [a great article](https://dev.to/craigmichaelmartin/the-problem-with-promises-in-javascript-5h46) on the pitfalls of `Promise`s in JavaScript.

_Definitely_ read that article for a much better explanation on this than I could provide you. I'll quote him directly from the Intro of his article there with his three biggest issues with Promises:

> - Promises have an API which encourages casually dangerous code
> - Promises lack a convenient API to safely work with data.
> - Promises co-mingle rejected promises and unintended runtime exceptions

We always have tradeoffs in writing code and a `Promise` is no different.

### The Good

Using a `Promise` can result in some more succinct, clean code than nesting callbacks. Take that with a grain of salt because again we've all got our preferences and that may not be true for all!

I also believe it's easier to debug and separate concerns as we can modularize our code a little more when converting to Promises.

I took just the first part of the above **callback** example and converted it to return a `Promise`:

```js
const readDirPromise = (source) => {
  // Return a new Promise
  return new Promise((resolve, reject) => {
    // Do the actual file system read
    fs.readdir(source, (err, files) => {
      // If there's a problem reject with error thrown
      if (err) reject(err)
      // Else we succeed and resolve with the files
      resolve(files)
    }
  })
}

readDirPromise(source)
  .then(data => {/* Do something with data */})
  .catch(err => {/* Handle error */}
```

This way we don't have to nest a whole lot. We place this piece of logic in a function that will either **resolve** and move on or **reject** and show us the error. 

If we continued to convert that _whole callback example_ we would simply take each part with a specific task and place that inside its own function like `readDirPromise` and call those in the `then()` statements.

```js
readDirPromise(source)
  .then(data => {/* secondStep() */})
  .then(data => {/* thirdStep() */})
  .then(data => {/* fourthStep() */})
  .catch(err => {/* Handle error */}
```

Each time we chain `then()`s we're expecting the last step to **resolve** or **succeed**. If at any point _there is an error_ our single `catch()` statement will handle it instead of having an `if (err)` a whole bunch. Then we can place the code for handling each type of error in just the `catch()`.

## Final Thoughts

I think `Promise`s are great and want to do more with them. _That said_ I worked with **callbacks** first and certainly have an appreciation for them. Especially in simpler tasks it might be easier just to use **callbacks**. Once more they're present in **a lot of code** because that was the way to achieve our asynchronous goals in the dark pre-ES6 days.

Having a solid understanding of the foundational pieces of JavaScript, in my mind, helps us understand and appreciate all the awesome, new features we've gotten in just the last handful of years.