Promises in Javascript

What is promises?
Promises are Javascript objects that wrap a certain operation that can be an asynchronous operations or even synchronous operations. They define a step-by-step chain for Javascript operations. For example: fetch(). Promises are often used to handle asynchronous operations in JavaScript. They represent the eventual completion (or failure) of asynchronous operation/s and their resulting value.
Promises are returned objects to which you attach callbacks, instead of passing callbacks into functions.
They are easy to manage when dealing with multiple asynchronous operations where callbacks can create callback hell leading to unmanageable code. Prior to promises, events and callback functions were used but they had limited functionalities and created unmanageable code. Multiple callback functions easily lead to long and unmanageable code and events were not good at handling asynchronous operations.
Example of a simple callback hell:
el.addEventListener('click', function() {
setTimeout( function() {
doSomething( function() {
doSomethingElse( function() {
...
});
});
}
}
In the above example, there is three nestedfunction() {} statements, which can be hell to understand, debug and maintain.
Promises are the ideal choice for handling asynchronous operations in the simplest manner. They can handle multiple asynchronous operations easily and provide better error handling than callbacks and events.
The same above code can be simplified as such:
addListener(el, 'click')
.then( function() {
return setTimer(2000);
})
.then( function() {
return doSomething();
}
.then( function() {
return doSomethingElse();
});
PS: 2 new methods, addListener and setTimer (not native to the Javascript engine) have been added for simplicity.
Promise in action
A basic example of promise is:
const myPromise = new Promise((resolve, reject) => {
});
Promise is a constructor function, so you need to use the
newkeyword to create one.
A more concise example is:
const promise = fetch('https://jsonplaceholder.typicode.com/todos/1', { })
.then(function(response) { // then always produces a new promise and thus permits chaining more then
return response.json(); // returns a promise which resolves with the result of parsing the body text as JSON
})
.then(function(data) { // the return from the first promise is passed as argument onto the second promise
console.log(data);
});
In the above example, we do not console.log(data) in the first then block as it is an asynchronous operation. response.json() returns a promise which resolves with the result of parsing the body text as JSON. This promise is passed to the second then statement to console log it.
You may argue that there is no
newkeyword used. This is because thefetchmethod is already returning a promise.
Deeper into Promise
A Promise has three states:
- resolved/fulfilled: Action related to the promise completed succeeded
- rejected: Action related to the promise failed and the promise is rejected
- pending: Promise is still pending i.e not fulfilled or rejected yet
The promise is considered settled when either resolved or rejected, else it stays in the pending state.
Example of pending state:
new Promise ( function( resolve, reject) {
setTimeout( function() {
... // no resolve or reject operation, thus this promise stays as pending
}, 2000);
});
Example of resolved state:
const myPromise = new Promise ( function( resolve, reject) {
setTimeout( function() {
console.log('first operation');
resolve('first operation'); // Can accept parameters to pass them to the next then statement
}, 2000);
});
Example of reject state:
const myPromise = new Promise ( function( resolve, reject) {
setTimeout( function() {
console.log('first operation');
resolve('first operation');
}, 2000);
});
The then statement in promise
Promises are most useful when you have a process that takes an unknown amount of time in your code (i.e. something asynchronous), often a server request. When you make a server request it takes some amount of time, and after it completes you usually want to do something with the response from the server. This can be achieved by using the then method. The then method is executed immediately after your promise is fulfilled with resolve. Here’s an example:
myPromise.then(result => {
// do anything with the result (the resolved data)
});
result comes from the argument given to the resolve method. In other words, data passed from the resolve() statement will then be passed to the next and this operation will be invoked only after the first function is resolved
PS: Any return from a
thenfunction is available to the next and only to the nextthenfunction as parameter for it. (No need to pass the data in the resolve function as it is the case in the above example)
myPromise
.then( function(data) {
console.log(data + ' and then the second operation');
return 12345;
})
.then( function(data) {
console.log(data); // this will console: '12345'
});
Handle the reject state in Promise
We can either use the function(error) argument or the '.catch' method.
Example of rejected state with the reject:
const myPromise = new Promise ( function( resolve, reject) {
setTimeout( function() {
console.log('first operation');
reject('first operation'); // reject instead of resolve here
}, 2000);
});
myPromise
.then( function(data) {
console.log(data + ' and then the second operation');
return 12345;
}, function(error){ // once a reject is reached, the Javascript engine will try to find the closest error function and execute it instead.
//handle error
})
.then( function(data) {
console.log(data); // this will console: '12345'
});
Example of rejected state with the .catch:
const promise = fetch('https://jsonplaceholder.typicode/todos/1', {}) // .com is omitted purposely in the url to fail the API call
.then(function(response) {
return response.json();
})
.catch( function(err) { // trigger this statement instead of the above then
console.log('Failed response!');
})
.then(function(data) { // this one will trigger as this is a promise.
console.log(data);
});
If you wish to prevent any then execution if a reject state is reached, simply move the
.catchstatement to the end. The Javascript engine will search for the closest catch statement and will jump directly to it.




