loading...

Can't be dishonest when you make a Promise.

michael_bazile profile image Michael Bazile ・6 min read

Welcome. How are you doing? I hope you're doing great. If you're not, after reading this blog I can promise that you will. Just hold onto that promise until then.

To get things started, let's have a quick recap about JavaScript, in particular how JavaScript is interpreted. When coding in JavaScript, it's important to note that when a program is run, it is run synchronously. This is because JavaScript is a single-threaded language and will only execute code one statement at a time. This becomes an issue when you need to have multiple things happening at the same time. Be it either sending HTTP requests to a server, dealing with event handlers or any other asynchronous task that will take an undetermined amount of time to complete. If a user comes to your application and has to constantly wait to interact with different parts of the page, you can guarantee that the user is going to leave your page and never come back.

In comes asynchronous JavaScript. This concept allows for your program to run, and when an asynchronous action occurs, your program will still run without being blocked. When the action is finished, the program will be notified and then gain access to the results of that action. Prior to Promises, developers would use callbacks to handle asynchronous tasks in JavaScript. Take this code snippet for example. In which order do you think the following will be run?

function greeting(){
console.log('Hey! How are you?');
console.log('Nice to see you');
}

console.log('Hi, there');

greeting();

console.log('Okay, have to go now!')
Enter fullscreen mode Exit fullscreen mode

This will be the order in which the code will be run:

'Hi, there'
'Hey! How are you?'
'Nice to see you'
'Okay, have to go now!'
Enter fullscreen mode Exit fullscreen mode

Notice how once we hit the function call of greeting, the interpreter had to block the thread to execute everything inside of that function. If it were the case that a web page was filled with synchronous code like that, it will make the user experience terrible. It will also make your web page or application very inefficient. Enter asynchronous callbacks. Let's take the same example from above and make it asynchronous. Let's look at the code snippet below, and can you guess the order in which the following will be executed?

function greeting(){
console.log('Hey! How are you?');
console.log('Nice to see you');
}

console.log('Hi, there');

setTimeout(() => { 
greeting()
}, 0); 

console.log('Okay, have to go now!')
Enter fullscreen mode Exit fullscreen mode

This will be the order in which the code will be run:

'Hi, there'
'Okay, have to go now!'
'Hey! How are you?'
'Nice to see you'
Enter fullscreen mode Exit fullscreen mode

Notice how this time the interpreter did not have to wait to execute greeting function? The interpreter got to the greeting function and sent that function call to what is referred to as the event loop. Then once the last statement on the page finished executing, the console logs from greeting were printed to the console. One piece of context, setTimeout is used here to mimic the behavior of an HTTP request, or any asynchronous tasks may that an undetermined amount of time to complete.

There are a few different things at play there. However, to give you an overview of why asynchronous code is so important in JavaScript is that example right there. The thread did not have to stop at any point in execution. The interpreter reads every statement from top to bottom, and if any asynchronous tasks appear the interpreter will wait until every other statement on the page is ran to execute that function.

This is all possible with the help of what is called the event loop. The event loop allows for the asynchronous model that JavaScript relies on to produce fluid and non-blocking applications.

Asynchronous callbacks are amazing, they enable JavaScript to be dynamic and non-blocking and all the sort. The only issue comes in when we need to code out a plethora of asynchronous callbacks one after another. Take the following code snippet for example:

 if (req.method === 'POST') {
    //parse chunks
    let body = '';
    req.on('data', (chunk) => {
      body += chunk.toString() + '\n';
    });
    req.on('end', () => {
      let url = querystring.parse(body).url;
      archive.isUrlArchived(url, (exists) => {
        if (exists) {
          httpHelpers.serveAssets(res, path.join(archive.paths.archivedSites, url));
        } else {
          archive.isUrlInList(url, (exists) => {
            if (!exists) {
              archive.addUrlToList(url, () => {
                res.writeHead(302, httpHelpers.headers);
                httpHelpers.serveAssets(res, path.join(archive.paths.siteAssets, '/loading.html'));
              });
            }
          });
        }
      });
    });
  }
};
Enter fullscreen mode Exit fullscreen mode

The code example above is an example of a node.js server setting up routes from an incoming HTTP POST request. This request is handling multiple different conditions, and with each condition, in order to receive the information that the request is asking for, we have to go out and get that information using asynchronous functions, and then bring it back to the client. In order to implement this without blocking the thread, we have to have a callback function in place to wait for the data to get back to the server then manipulate the data from there.

This is perfectly fine and will work, the problem with asynchronous callbacks is when your application grows in size and you need to include more and more callbacks. This makes your code hard to read and maintain not only for you, but the next person that may come and read your code. People normally identify and reference this pattern as callback hell. That's something that needs to be avoided if possible. Enter promises.

Just as the name suggests, promises are a declaration or assurance that one will do a particular thing or that a particular thing will happen at some point in the future, with thing being asynchronous functions. Promises in JavaScript are extremely useful for asynchronous tasks because they abstract away the exact time data will become available, and simply leave us with the task of handling that data if/when it comes back from that asynchronous function.

Coming from MDNs web docs, promises in JavaScript are objects that represent the eventual completion (or failure) of an asynchronous operation, and its resulting value. When implementing promises in JavaScript, it's important to note that a promise will be in one of four stages:

1.) pending - Hasn't fulfilled or rejected yet
2.) fulfilled - The action relating to the promise succeeded
3.) rejected - The action relating to the promise failed
4.) settled - Has fulfilled or rejected
Enter fullscreen mode Exit fullscreen mode

Let's take a look at an asynchronous promise in action:

let promise = new Promise((resolve, reject) => {
//conditional is here to handle errors that may happen
   if (error) {
  reject(error);
   }
//set timeout is here to mimic an asynchronous task
    setTimeout(function() { 
        resolve('Hi, my name is');
    }, 2000);
});
Enter fullscreen mode Exit fullscreen mode

In this example, we created an instance of a promise using the new keyword and assigned that to a variable. The promise constructor function takes a callback function as an argument, and that callback function takes two parameters which are resolve and reject. Reject is called if there was an error at any point in execution, and resolved is called there weren't any errors in execution.

We can now use the promise like so:

promise
.then((data) => {
    console.log(data);
})
.then((data) => {
    console.log('Hi, my name is');
})
.then((data) => {
    console.log('Hi, my name is');
})
.then((data) => {
    console.log('errka errka slim shady');
})
.catch((error) => {
console.error(error);
});
Enter fullscreen mode Exit fullscreen mode

The function above will log:

1.) 'Hi, my name is'
2.) 'Hi, my name is'
3.) 'Hi, my name is'
4.) 'errka errka slim shady'

The .then(), and .catch() are properties on the promise object that we can chain to manipulate exactly what happens if the promise, either is rejected or resolved. You can equate chaining to saying, "Hey promise, go get this thing for me while I go to the store, and if/when you get that thing I'll handle it from there."

So in conclusion, when using promises we can still get all the fantastic benefits of handling multiple asynchronous tasks without having to go through callback hell to do it. I hope after reading this blog you'll get at least a little bit better understanding of what promises are and why they are so important and amazing in JavaScript. I promise you won't regret it.

Posted on by:

Discussion

pic
Editor guide