DEV Community

Cover image for Javascript Promise
Barri
Barri

Posted on

Javascript Promise

Javascript Promise is one of the features that was rolled out in ES6 in 2015. Promises in Javascript operate within the same premise as a promise in real life. As a person can make a promise, so can Javascript.

For example, I could say, "I promise to learn Javascript Promise this year".

Now promises aren't just made. When we make promises, we strive to fulfil them and sometimes we fail. If I learn Javascript this year, then I will keep my promise. If I don't, then I failed to keep that promise.

So a promise ends up either being fulfilled and kept or unfulfilled and failed. These terms are translated in Javascript as resolved or rejected. A fulfilled promise is a resolved one, while a failed one is a rejected promise.

Callbacks and Asynchronous actions

Before we delve fully into promise, we will look at callbacks and asynchronous programming. Why do we need to know about callbacks and asynchronous programming? Because they are tied to promises and promises are one of the ways to deal with them.

A callback function is a function that is written as an argument and passed to another function (the outer function), and invoked there. The passed argument can be executed by the outer function immediately or later. Javascript supports callbacks and implements them in subroutines, lambda expressions, blocks or function pointers.

There are two types of callbacks named based on how they control data flow at runtime. They are synchronous callbacks which are the default callbacks or deferred callbacks, also called asynchronous callbacks.

Synchronous callbacks are the callbacks that are executed immediately while asynchronous callbacks are executed later.

Asynchronous in Javascript refers to two events or threads happening at the same time, simultaneously. Not waiting for the previous one to complete. In asynchronous programming, a program can make a request from another software or server and do other things in the background while it waits for a reply.

Javascript Promise and why it is important.

A Javascript promise is an asynchronous process that produces an unknown value at a later time. According to MDN "A Promise is a proxy for a value not necessarily known when the promise is created. It allows you to associate handlers with an asynchronous action's eventual success value or failure reason."

Javascript is single threaded. This means two or more scripts cannot run at the same time. They always have to run one after the other. This makes programming with it slow, time consuming and monotonous. Once a network sends a request, it can do nothing else until it receives a response. Only one request is sent at a time.

Objects and events in programming try to emulate human behaviour and Javascript's single threading does not replicate human behaviour. Humans are multi-threaded. We perform different actions simultaneously and asynchronously. A human can talk and cook at the same time. We can type with both hands.

Promises, callbacks and asynchronous programming are a great way to perform multi-thread actions. Multiple requests and time-consuming actions can be performed simultaneously without blocking the main thread.

Features of Javascript Promise

  • 1. A promise is asynchronous in nature.
  • 2. It has two default callbacks - reject and resolve.
  • 3. The output, value or result is unknown at the time of creation.
  • 4. It is a good way to overcome the issues that arise from simple and nested callbacks.
  • 5. The result of a promise is immutable. It's state cannot be changed.

Syntax of a Javascript promise

A promise object is created by using the new keyword followed by the keyword - Promise. It takes function and one one argument. This argument takes two default callbacks - reject and resolve.

var dosomething = new Promise(function(resolve, reject) {
 // code goes here

 if (/* expression goes here */) {
   resolve("Yes, this works!");
 }
 else {
   reject("Oh No! It didn't work");
 }
});
Enter fullscreen mode Exit fullscreen mode

Resolved holds the output for a successful promise while reject holds the output for an unsuccessful promise.

.then() and .catch() handlers

.then() and .catch() are two two handlers attached to callbacks in promises. .then() is attached to a resolved promise while .catch() is attached to a rejected promise.
Let us look at demos on how to use promises and their handlers.

<body>
<script>
let man = new Promise(function(resolve, reject){

    man_full = true;
    if(man_full)
     resolve()
     else
     reject()

});
   man.then(function(){
       document.write("Stop Eating!")
   }).catch(function(){
       document.write("You stop eat more!")
   })

   //output: Stop Eating
   //if we change the value of man_full to false, it becomes 'You should eat more'
</script>
</body>
Enter fullscreen mode Exit fullscreen mode

Promises work with arrow functions which were also implemented in ES6. In the next demo, we will use an arrow function with parameters to give custom outputs.

<body>
<script>
let sum = new Promise((resolve, reject) =>{
    let add = 24 + 24;
    if (add == 49){
        resolve('Correct Answer!')
    }
    else {
        reject('Incorrect!')
    };
});

sum.then((custom_msg) =>{
    document.write("This is a "+ custom_msg)
}).catch((custom_msg) => {
    document.write("This is " + custom_msg)
});

//the custom msg with resolve callback is appended to the message for .then()
//the custom msg with reject callback is appended to the message for .catch()
        </script>
</body>

Enter fullscreen mode Exit fullscreen mode

Static methods of promises

Promise.all()
This is a promise API that executes two or more promises in parallel. For example, if we have to download certain files all at once, it will process them and give a value once they are all resolved.
It takes an array of promises and returns a new promise. The new promise is the value

<body>
<script>
const download_one = new Promise((resolve, reject) =>{
    setTimeout(() => {
        resolve('First download completed');
    }, 5000);
})


const download_two = new Promise((resolve, reject) =>{
    setTimeout(() => {
        resolve('Second download completed');
    }, 3000);
})
const download_three = new Promise((resolve, reject) =>{
    setTimeout(() => {
        resolve('Third download completed');
    }, 1000);
})

Promise.all([
     download_one,
     download_two,
     download_three
]).then((msgs) => {
     document.write(msgs);
}).catch((msgs) => {
     document.write(msgs);
})

</script>
</body>
Enter fullscreen mode Exit fullscreen mode

It takes about 5 seconds for the output to display since the longest time is 5 seconds for the first download. The order of the output appears the same way as they are written even though the first download takes the longest to process.

This shows just how promise.all() works, executing and processing actions in parallel. If for any reason any of the promises listed is failed or rejected, promise.all() rejects everything and the error message is displayed.

const download_one = new Promise((resolve, reject) =>{
    setTimeout(() => {
        resolve('First download completed');
    }, 5000);
})

const download_two = new Promise((resolve, reject) =>{
    setTimeout(() => {
        reject(new Error('Error with second download'));
    }, 3000);
})

const download_three = new Promise((resolve, reject) =>{
    setTimeout(() => {
        resolve('Third download completed');
    }, 1000);
})

Promise.all([
     download_one,
     download_two,
     download_three
]).then((msgs) => {
     document.write(msgs);
}).catch((msgs) => {
     document.write(msgs);
})
Enter fullscreen mode Exit fullscreen mode

Promise.allSettled()
Since promise.all() takes an 'all or nothing' approach where it rejects the entire list if one promise is rejected, promise.allSettled() takes a more relaxed approach. It insteads shows the status of each promise according to the value or result.
Fulfilled promise = result
Rejected promise = error

<body>
<script>
const download_one = new Promise((resolve, reject) =>{
    setTimeout(() => {
        resolve('First download completed');
    }, 5000);
})

const download_two = new Promise((resolve, reject) =>{
    setTimeout(() => {
        reject(new Error('Error with second download'));
    }, 3000);
})

const download_three = new Promise((resolve, reject) =>{
    setTimeout(() => {
        resolve('Third download completed');
    }, 1000);
})

Promise.allSettled([
     download_one,
     download_two,
     download_three
]).then((msgs) => {
     console.log(msgs);
}).catch((msgs) => {
     console.log(msgs);
})

/*
Output
0:{status: "fulfilled", value: "First download completed"}
1: {status: "rejected", reason: Error: Error with second download}
2: {status: "fulfilled", value: "Third download completed"}
*/
</script>
</body>
Enter fullscreen mode Exit fullscreen mode

Promise.race()
promise.race() returns an output once one of the promises is completed. Unlike promise.all(), it doesn't wait for the entire promises listed to be completed before it returns a value. It doesn't matter if that promise is a fulfilled or rejected promise.

<body>
<script>

const download_one = new Promise((resolve, reject) =>{
    setTimeout(() => {
        resolve('First download completed');
    }, 5000);
})

const download_two = new Promise((resolve, reject) =>{
    setTimeout(() => {
        reject(new Error('Error with second download'));
    }, 3000);
})

const download_three = new Promise((resolve, reject) =>{
    setTimeout(() => {
        resolve('Third download completed');
    }, 1000);
})

Promise.race([
     download_two,
     download_one,
     download_three
]).then((msgs) => {
     console.log(msgs);
}).catch((msgs) => {
     console.log(msgs);
})

//OUTPUT
//Third download completed
</script>
</body>
Enter fullscreen mode Exit fullscreen mode

The output says 'Third download completed', because the value for the third download is displayed first, since it has the shortest settimeout.

Promise.any()
Promise.any() behaves similarly to promise.race(), but it picks the first fulfilled or resolved promise to display.

<body>
<script>
const download_one = new Promise((resolve, reject) =>{
    setTimeout(() => {
        resolve('First download completed');
    }, 5000);
})

<body>
<script>
const download_two = new Promise((resolve, reject) =>{
    setTimeout(() => {
        reject(new Error('Error with second download'));
    }, 1000);
})

const download_three = new Promise((resolve, reject) =>{
    setTimeout(() => {
        resolve('Third download completed');
    }, 3000);
})

Promise.any([
     download_two,
     download_one,
     download_three
]).then((msgs) => {
     console.log(msgs);
}).catch((msgs) => {
     console.log(msgs);
})

//OUTPUT
//Third download completed

</script>
</body>
Enter fullscreen mode Exit fullscreen mode

The second download was reset to have the shortest timeout of 1 second. But because it is a failed promise, it wasn't acknowledged and the second shortest - the third download was picked.

States of a Promise

A promise has four states or behavior

  1. Pending: The pending state of a promise is the waiting state of the promise. This is the state when we neither have a resolved nor rejected promise.
  2. Fulfilled: This is an asynchronous operation that was completed successfully. It returned a value or a fulfilled promise.
  3. Rejected: An asynchronous operation was not successful. It returned an error and a rejected promise.
  4. Settled: This is a processed promise that has been rejected or fulfilled. It is the final outcome of an asynchronous promise operation. A settled promise is immutable.

Conclusion

Using promise with asynchronous operations has many benefits. It flattens the code, makes it more readable and maintainable. It also helps to handle asynchronous operations better, much better than callbacks.
Promise is a modern object and it works well with modern browsers, but if browser compatibility issues arise, these could be overridden with polyfills.

Top comments (0)