DEV Community ๐Ÿ‘ฉโ€๐Ÿ’ป๐Ÿ‘จโ€๐Ÿ’ป

Cover image for JavaScript: Promise
Kiran Raj R
Kiran Raj R

Posted on • Updated on

JavaScript: Promise

A promise is an object representing the eventual completion or failure of an asynchronous action. A promise can be a "place holder" for a result that may be available in the future. The result can be successful or reason to the failure, one thing is promised, there will be a result in the future. Promise helps asynchronous method's to return values like a synchronous method.

The constructor syntax for promise object is

let promise = new Promise( function(resolve, reject){
       // executor
) 
Enter fullscreen mode Exit fullscreen mode

When a promise is created the executor(function passed to the promise) is called automatically, the executor is responsible for producing a result. The Executor function takes two arguments, resolve and reject. They are callbacks that is produced by the JavaScript engine. When the executor produce result, one of the callback is called based on the type of the result(success or failure). If the result is successful resolve is called if the result is the reason for failure, the reject callback is called.

There are two internal properties of object returned by the promise constructor, state and result. When the promise is created the state will have a value pending and result will be undefined. The state can change to fulfilled with a result value or it can change to rejected with result error. The internal values cannot be accessed directly. The promise which is either resolved or rejected is called settled, any state change is final and all further calls are ignored.

An example of a promise implementation is given below

const demoFunction = function(x ,y){
    return new Promise(function(resolve ,reject) {
        if (x + y < 100){
            resolve("The value is normal");
        }else {
            reject("Value is abnormal");
        } 
    });
};

function onSuccessful(result){
    console.log(result);
}

function onFailure(result){
    console.error(result);
}

demoFunction(30, 40).then(onSuccessful, onFailure); 
// Output : The value is normal

demoFunction(400, 40).then(onSuccessful, onFailure); 
// Output : Value is abnormal
Enter fullscreen mode Exit fullscreen mode

In the above code snippet we created a new promise demoFunction which will return a promise with a result depending on the condition in the if statement. We declared two functions (callbacks) onSuccessful, onFailure to handle the settled promise's result. onSuccessful to handle successful result and onFailure to handle failure result. We use then method to attract the callbacks to the promise.

To handle the result of a promise we can use

  1. then() method
  2. catch() method
  3. finally() method

then() method takes two callback functions as arguments. First one is called when the promise produce a successful result i.e. when the state is resolved. The second callback is called when the state is rejected.
Example : demoFunction(30, 40).then(onSuccessful, onFailure);

catch() method is used to handle the rejected state it is similar to .then(null ,onFailure). Catch have only one argument, a callback to handle the rejected state.

finally() method takes no arguments and it will runs statements if the promise is settled, it will not care if the state is resolved or rejected. Finally is normally used to do cleanup works like disconnect a database connection. Finally passed the results to the next handler, it will not process the result. An example is given below.

const demoFunction = function(x ,y){
    return new Promise(function(resolve ,reject) {
        if (x + y < 100){
            resolve("The value is normal");
        }else {
            reject("value is abnormal");
        } 
    });
};

function onSuccessful(result){
    console.log(result);
}
function onFailure(result){
    console.error(result);
}

demoFunction(400, 40).catch(onFailure); // value is abnormal
demoFunction(400, 40)
    .finally(()=>{ console.log("The promise is settled");})
    .catch(onFailure) 
     // Output : The promise is settled  
                 value is abnormal
Enter fullscreen mode Exit fullscreen mode

Promise Chaining

When we create a promise using the promise constructor. it returns a promise with a result. The result can be a value or a reason for an error. In order to handle the returned promise we use then() methods or catch() methods or finally() methods. These methods return a promise, we can call new handler to it, if needed. Adding new handlers to previous handlers is called chaining. Like stacking books one over another.

A code snippet of a chaining example is given below.

new Promise(function (resolve, reject){
    resolve(10);
})
.then( function(result){
    console.log(result);      // 10
    result = result * 10;   
    return result
}).then( function(result){
    console.log(result);      //100
    result = result * 10;  
    return result
}).then( function(result){ 
    console.log(result);      //1000 
    result = result * 10;   
    return result
});
Enter fullscreen mode Exit fullscreen mode

We must add the handler to the result / return value of the previous handler in order to be a chain. If we add multiple handlers to a same promise, all handlers will act on the result of the same promise and it is not chaining.

//Chaining
let p1 = new Promise(function (resolve, reject){
    resolve(10);
});

let p2 = p1.then( function(result){
    console.log(result);      // 10
    result = result * 10;   
    return result
});
let p3 = p2.then( function(result){
    console.log(result);      //100
    result = result * 10;  
    return result
});
let p4 = p3.then( function(result){ 
    console.log(result);      //1000 
    result = result * 10;   
    return result
});
Enter fullscreen mode Exit fullscreen mode

Let me explain it with a simple example. Let variable p1 be a promise created using Promise constructor. We store return value of p1.then() into another variable p2. Remember p2 stores the return value of p1.then() not the return value of p1. Now we create another variable p3 which stores the return value of p2's handler(p2.then()) and one more variable p4 which contains the return value of p3's handler(p3.then()). Here p2, p3, p4 are not directly connected to the promise p1. Their relation is shown below

new Promise --> p1
p1.then() --> p2
p2.then() --> p3
p3.then() --> p4

The variables depend on the previous handlers, not to the promise p1, it can be also written as p1.then--> .then() --> .then(). since the handlers are stacked as one after another, the code represents a promise chain.

The below code snippet is not chaining as the variables pp2, pp3 and pp4 stores the handler of promise pp1. They all are operating with the same result they got from the handler then of promise pp1.

// Not chaining
let pp1 = new Promise(function (resolve, reject){
    resolve(10);
});
let pp2 = pp1.then( function(result){
    console.log(result);      // 10
    result = result * 10;   
    return result
});
let pp3 = pp1.then( function(result){
    console.log(result);      //10
    result = result * 10;  
    return result
});
let pp4 = pp1.then( function(result){ 
    console.log(result);      //10
    result = result * 10;   
    return result
});
Enter fullscreen mode Exit fullscreen mode

Let me show one more code snippet.

var promise1 = new Promise(function (resolve, reject){
    resolve(10);
});
console.log(promise1); 
// __proto__: Promise
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: 10

let promise2 = promise1.then(function(result){
    console.log(result);     //10
    return result * 2;
});

console.log(promise2);
// __proto__: Promise
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: 20

promise2.then(function(result){
    console.log(result);     //20 
    return result * 2;
});

let promise3 = promise2.then(function(result){
    console.log(result);     //20
    return result * 2;
});

console.log(promise3);
// __proto__: Promise
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: 40

Enter fullscreen mode Exit fullscreen mode

I divided the code into two parts, let's look at part one.

var promise1 = new Promise(function (resolve, reject){
    resolve(10);
});
console.log(promise1); 
// __proto__: Promise
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: 10

let promise2 = promise1.then(function(result){
    console.log(result);     //10
    return result * 2;
});

console.log(promise2);
// __proto__: Promise
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: 20
Enter fullscreen mode Exit fullscreen mode

First we create a promise using Promise constructor and assign the value to a variable promise1. We then store the result of the that promise's handler to a new variable promise2. We can see that the result has a value of 10. We multiply the result by 2 and return the result, it is stored in the promise2 variable. When we look at the promise2 we can see that the result is 20 and state is fulfilled. Which shows that the handlers code worked. Let's look at part two.

promise2.then(function(result){
    console.log(result);     //20 
    return result * 2;
});

let promise3 = promise2.then(function(result){
    console.log(result);     //20
    return result * 2;
});

console.log(promise3);
// __proto__: Promise
// [[PromiseState]]: "fulfilled"
// [[PromiseResult]]: 40
Enter fullscreen mode Exit fullscreen mode

We add a then handler to the promise2 and multiply the result by 2 and returned. But this return has no use, why?? It is not saved or stored, we only alter the value and returned, the altered value is not stored to be used by another handler. Is it true?? Let's look, in the next line we do the same as previous but this time we stored the return value in variable promise3. From the console log statement inside the promise2.then we can see that the current value of result is 20. i.e. the previous then handler has no impact on the result. If the code was like

promise2.then(function(result){       //first then
    console.log(result);     //20 
    return result * 2;
}).then(function(result){             //second then
    console.log(result);     //40
    return result * 2;
});

then the first *then* result has impact on the second *then*, as you can see the console.log statement in the second then shows result as 40. 
Enter fullscreen mode Exit fullscreen mode

Some important things to remember

  1. Handlers return promise.
  2. Handlers can be used to handle promises returned by handlers.
  3. Assigning multiple handlers to a promise is not chaining.
  4. Handlers in chain will wait for the previous promise to be settled.

Top comments (0)

๐ŸŒš Friends don't let friends browse without dark mode.

Sorry, it's true.