How I use JavaScript Promises
Tags: beginners, javascript
Published: 2019-07-25 17:38:24

_Originally published at [Shubho.Dev](https://www.shubho.dev/coding/how-i-use-javascript-promises/)_

Async programming in JavaScript was scary for me. The only async paradigm I was comfortable with was jQuery's `$.ajax`. However, I went full vanilla JavaScript for the past 8 years, and when I started working with NodeJS, I had to learn Promises. I haven't dabbled much with third-party libraries like Bluebird. I have the most experience with native Promise.

My main issue with `Promise` (or asynchronous paradigm in general) used to be when I wanted to execute statements after the Promise statement began. It took some time to realise that once a Promise statement fires, there is no way to cancel it. Another issue was Promise chaining. This one was a kicker. My earlier functions with Promises always looked like friends of callback hell. After all these years and working on a couple of big projects, I can safely say I love Promises. Even though async/await is the new fad, I still love Promises.

So here is how I use Promises to make my coding life simpler.

## Create a Promise skeleton

Whenever I create a new function that returns a Promise, I create the skeleton first. Since the function cannot return anything other than Promise based values, I always wrap all the statements of the function within the Promise.

```javascript
function sample() {
    return new Promise(function(resolve, reject) {
        // The function body
    });
}
```

The above sample function wraps its entire statement within the Promise, returning immediately. You can either `resolve()` or `reject()` the output you want from the body. This way, I never make a mistake of not returning a Promise. It also helps me in creating Promise chains. Whenever in a chain, I realise I need a new function, I create the skeleton name it appropriately and finish the main chain. Then I come back one by one and finish the individual functions.

## Promise chains - Points to remember

Promise chaining is tricky. If we are not careful, we can have a new type of callback hell. An example:

```javascript
function promiseCallback() {
    return new Promise((resolve, reject) => {
        aNewFunction()
            .then((values) => {
                someOtherFunction(values)
                    .then((someOtherValue) => {
                        // Do something
                        resolve(someOtherValue);
                    })
                    .catch((err1) => {
                        // Error in inner function
                        reject(err1);
                    });
            })
            .catch((err) => {
                // Error in outer function
                reject(err);
            });
    });
}
```

In the above sample `aFunction()` and `someOtherFunction()` are two functions returning Promises. If you see carefully, the sequence looks like a callback hell. The inner then and catch the chain, and outer ones are independent. We cannot handle errors in a common catch block, and we need to be careful that the inner functions are always the last line within their outer `then()` otherwise we can't control the execution flow.

A better way with chains:
```javascript
function promiseCallback() {
    return new Promise((resolve, reject) => {
        aNewFunction()
            .then((values) => {
                return someOtherFunction(values);
            })
            .then((someOtherValue) => {
                // Do something
                resolve(someOtherValue);
            })
            .catch((err) => {
                // Error in outer function
                reject(err);
            });
    });
}
```

Returns within the `then` chain can only have three types:
1.  **Promise** - A `then` function in a chain can return a Promise. It's result is passed to the next `then`.
2. **Scalar Value** - A `then` function in a chain can return a  value like a String or a Number. This value is passed to the next `then` as is and the chain can continue.
3. **Throw** - A `then` function can `throw` an error, which moves the execution to the catch block.

As long as all your returns within a `then` follow the above three types, you shouldn't have issues following your Promise chain.

**Note**
Remember to always `resolve()` or `reject()` in the last `then` or `catch` of the chain.

## When to create a new Promise function

Within a Promise chain, if there are multiple if-else conditions, and each condition can lead to different Promise results, it is an excellent time to create a new function that returns a Promise. This way, the Promise chain returns a single statement calling the new function.

## Handling a scalar value or a Promise function in one step

Assume we have a function which gets the marks attained by a student using his roll number. However, the function either takes a roll number as an input or the name of the student. The marks can be attained from the DB only using the roll number. Here is some pseudo-code.

```javascript
function getMarks(obj) {
    let rollNumberPromise = null;
    if ('rollNumber' in obj) {
        rollNumberPromise = Promise.resolve(obj.rollNumber);
    } else if ('studentName' in obj) {
        rollNumberPromise = getRollNumberFromName(obj.studentName);
    }

    if (!rollNumberPromise) {
        reject('Nothing worked');
    }

    rollNumberPromise
        .then((rollNumber) => {
            return get_marks_from_db(rollNumber);
        })
        .then((marks) => {
            resolve(marks);
        })
        .catch((err) => {
            reject(err);
        });
}

function getRollNumberFromName(studentName) {
    return new Promise(function(resolve, reject) {
        fn_to_get_roll_number_from_db(studentName)
            .then((rollNumber) => {
                resolve(rollNumber);
            })
            .catch((err) => {
                reject(err);
            });
    });
}

function fn_to_get_roll_number_from_db(studentName) {
    return new Promise(function(resolve, reject) {
        // some code
    });
}

function get_marks_from_db(rollNumber) {
    return new Promise(function(resolve, reject) {
        // some code
    });
}
```

`getMarks(obj)` takes an Object as an input. We create a local variable `rollNumberPromise`. If the rollNumber is already present, we save the value in the variable using `Promise.resolve()`. This creates a Promise which resolves when called with the value. If student’s name is sent, then we save the call to the function `getRollNumberFromName(studentName)` to the local variable. Calling `rollNumberPromise.then()` returns a rollNumber whether it is received from the DB or sent directly as input to the function. Using it this way ensures that `getMarks()` has a single Promise chain, rather than an if-else condition based on whether the input passed was a number or a name.

## Invoke a Promise at the end

As mentioned before, once a Promise, once invoked, cannot be cancelled. Any statements which do not depend on the Promise output and which can be carried out independently without an async call should complete before you start a Promise chain in your function. Once a Promise chain begins, any subsequent steps must be within the `then` chain. The only exception to this is when you do not care of the Promise value, and you want the Promise to execute in the background while your primary function keeps running.

## Conclusion

Promises are difficult. However, with practice and following some rules, it makes working with them a charm. I strictly follow the above rules, and I never go wrong with Promises these days. Find out what you are comfortable with and create your own rules.