ES6 came with many new features, but one of the best features was the official introduction of Promises. Promises allow you to write clean non-callback-centric code without ever having to worry about callback hell. Even if you never write your own promise, knowing how they work is incredibly important, since many newer parts of the JavaScript API use promises instead of callbacks. The core idea behind promises is that a promise represents the result of an asynchronous operation. A promise is in one of three different states:
- pending — The initial state of a promise.
- fulfilled — The state of a promise representing a successful operation.
- rejected — The state of a promise representing a failed operation.
A promise in JavaScript is the same promise that we make in our life. If I promise to do something for a friend then it has two results. If I fulfill that promise by working on it then he will be happy or say it’s resolved in terms of Javascript. And if the promise is never fulfilled by me then I failed to keep my promise or let’s say it’s not resolved in terms of JavaScript cause it’s been rejected to be done.
Let’s take an example:
First, we will look into the example which gets resolved,
let p = new Promise ((resolve,reject)=>{ let a = 1+1; if(a ==2){ resolve('Succeded') } else { reject('Failed') } }) p.then((message)=>{ console.log('Its resolved and '+message) }) .catch((message)=>{ console.log('Its rejected and '+message) })
Here, we create two-parameters inside the object of Promise(resolve, reject). We take ‘a’ as a promise and check whether it’s fulfilled or not. Line 11, we create “.then” which will call the message inside “resolve”. If we run the code then the condition is true because “promise a” value will be true i.e ‘a value equals to 2′. Since the condition gets satisfied We will get the message from ‘resolve’.
Another example which shows promise not being resolved,
Here, we create two-parameters inside the object of Promise(resolve, reject). We take ‘a’ as a promise and check whether it’s fulfilled or not. Line 11, we create “.then” which will call the message inside “resolve”. If we run the code then the condition is true because “promise a” value will be true i.e ‘a value equals to 2′. Since the condition gets satisfied We will get the message from ‘resolve’.
Another example which shows promise not being resolved,
Here, we create two-parameters inside the object of Promise(resolve, reject). We take ‘a’ as a promise and check whether it’s fulfilled or not. Line 11, we create “.then” which will call the message inside “resolve”. If we run the code then the condition is true because “promise a” value will be true i.e ‘a value equals to 2′. Since the condition gets satisfied We will get the message from ‘resolve’.
Another example which shows promise not being resolved,
let p = new Promise ((resolve,reject)=>{ let a = 1+2; if(a ==2){ resolve('Succeded') } else { reject('Failed') } }) p.then((message)=>{ console.log('Its resolved and '+message) }).catch((message)=>{ console.log('Its rejected and '+message) })
Here, we create two-parameters inside the object of Promise(resolve, reject). We take ‘a’ as a promise and check whether it’s fulfilled or not. Line 11, we create “.catch” which will call the message inside “reject”. If we run the code then the condition is false because “promise a” value will not be true i.e ‘a value not equals to 2’. Since the condition gets satisfied We will get the message from ‘reject’.
Callbacks:
A callback is a function called at the completion of a given task; this prevents any blocking and allows other code to be run in the meantime. Callbacks are the foundation of Node.js.Callbacks give you an interface with which to say, “and when you’re done doing that, do all this.” This allows you to have as many IO operations as your OS can handle happening at the same time. For example, in a web server with hundreds or thousands of pending requests with multiple blocking queries, performing the blocking queries asynchronously gives you the ability to be able to continue working and not just sit still and wait until the blocking operations come back.
Example of Callbacks:
function watchSuitsCallback(callback,errorCallback) { let userLeft = falselet userWatchingSuits = falseif (userLeft) { errorCallback({name: 'Episode completed', message: 'Shit'}) } else if (userWatchingSuits) { errorCallback({ name: 'User Watching Suits', message: 'With Harvey' }) } else { callback('Awesome') } } watchSuitsCallback(message => { console.log(message) },error => { console.log(error.name + ' ' + error.message) })
Here, We are using a very simple callback function which takes two callbacks i.e one for success and another is for error. Here the function checks whether two variables are true or not. If it’s true then the callback will be for ‘success’ message else it will call ‘error’ message.
Now we will take the same example using promises:
function watchSuitsPromise() { let userLeft = falselet userWatchingSuits = falsereturn new Promise((resolve, reject) => { if (userLeft) { reject({ name: 'Episode completed', message: 'Shit' }) } else if (userWatchingSuits) { reject({ name: 'User Watching Suits', message: 'With Harvey' }) } else { resolve('Awesome') } }) } watchSuitsPromise().then(message => { console.log(message) }).then(message => { console.log(message) }).then(message => { console.log(message) }).catch(error => { console.log(error.name + ' ' + error.message) })
Both the example looks the same except some syntax. But writing code is cleaner with Promises than with callbacks because if you start using nesting callbacks we might get in trouble as the code just keeps getting indented and indented.
But in Promises, You can create “.then” multiple times,
watchSuitsPromise().then(message => { console.log(message) }).then(message => { console.log(message) }).then(message => { console.log(message) }).catch(error => { console.log(error.name + ' ' + error.message) })
which is better than using multiple callbacks i.e callbacks inside of another and another or nesting callback also called callback hell. So, Promises are great and solve the inside looping problem with callbacks.
Async/Await:
One of the hardest things about writing good JavaScript is dealing with heavily nested asynchronous code. Promises were created to solve the problem with callback hell, but there are still plenty of nested problems related to promises. This is where async/await comes in. JavaScript added async/await to allows developers to write asynchronous code in a way that looks and feels synchronous. This helps to remove many of the problems with nesting that promises have, and as a bonus can make asynchronous code much easier to read and write.
let’s look at the following Example with Promises and compare using Async/Await,
function makeRequest(location){ return new Promise((resolve,reject)=>{ console.log(`making request to ${location}`) if(location == 'google'){ resolve('google says hello') } else{ reject('we can only talk to google') } }) } function processRequest(response){ return new Promise((resolve,reject)=>{ console.log('Processing response') resolve(`Extra Info ${response}`) }) } makeRequest('google').then(response=>{ console.log('Response received') return processRequest(response) }).then(processResponse=>{ console.log(processResponse) }) .catch(err=>{ console.log(err) })
The above code is similar to the codes which are shown earlier. Two functions, One for sending a request and another for the response to that request.
Now we will see the same code with Async/Await and see some differences,
function makeRequest(location){ return new Promise((resolve,reject)=>{ console.log(`making request to ${location}`) if(location == 'google'){ resolve('google says hello') } else{ reject('we can only talk to google') } }) } function processRequest(response){ return new Promise((resolve,reject)=>{ console.log('Processing response') resolve(`Extra Info ${response}`) }) } async function doWork(){ try{ const response = await makeRequest('google') console.log('Response received') const processResponse = await processRequest(response) console.log(processResponse) } catch(err){ console.log(err) } } doWork()
Here, we see a lot of differences in the code which also looks cleaner than code written with only ‘Promise’. We must wrap our code inside a function with an ‘async’ keyword before the function definition. Inside that function, code will be written in the try-catch block. ‘Try’ will take the code which might throw an error and ‘catch’ will send an error message if any error is caught. We must use await keyword before the functions that are going to be asynchronous. Otherwise, it will just return the promise but not the result of that promise being executed. Other than that Async/Await is pretty much similar to Promises. It makes easier for us to write promises and work with promises in our code.
You can further refer to the below links to learn more about Promises, Callbacks, and Async/Await…