We now have a promise that can either get resolved or rejected. If it gets resolved, meaning the promise was fulfilled, we have a function that handles that. If it gets rejected, we have a function that handles that as well. This is one of the reasons why promises are awesome. You get to provide different functions, depending on whether or not the promise got resolved or rejected. This lets you avoid a lot of complex if statements inside of our code, which we needed to do in app.js to manage whether or not the actual callback succeeded or failed.
Now inside a promise, it's important to understand that you can only either resolve or reject a promise once. If you resolve a promise you can't reject it later, and if you resolve it with one value you can't change your mind at a later point in time. Consider this example, where I have a code like the following code; here I resolve first and then I reject:
var somePromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hey. It worked!');
reject('Unable to fulfill promise');
}, 2500);
});
somePromise.then((message) => {
console.log('Success: ', message);
}, (errorMessage) => {
console.log('Error: ', errorMessage);
});
In this case, we'll get our success message printing to the screen. We'll never see errorMessage, because, as I just said, you can only do one of these actions once. You can either resolve once or you can reject once. You can't do both; you can't do either twice.
This is another great advantage over callbacks. There's nothing preventing us from accidentally calling the callback function twice. Let's consider the geocode.js file for example. Let's add another line in the if block of geocode request call, as shown here:
const request = require('request');
var geocodeAddress = (address, callback) => {
var encodedAddress = encodeURIComponent(address);
request({
url: `https://maps.googleapis.com/maps/api/geocode/json?address=${encodedAddress}`,
json: true
}, (error, response, body) => {
if (error) {
callback('Unable to connect to Google servers.');
callback();
This is a more obvious example, but it could easily be hidden inside of complex if-else statements. In this case, our callback function in app.js will indeed get called twice, which can cause really big problems for our program. Inside the promise example this callback will never get called twice, no matter how many times you try to call resolve or reject, this function will only get fired once.
We can prove that right now by calling resolve again. In the promise example case, let's save the file with the following changes:
var somePromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hey. It worked!');
resolve();
reject('Unable to fulfill promise');
}, 2500);
});
Now, let's refresh things; we'll resolve with our message, Hey. It worked! and we'll never ever have the function fired a second time with no message. Because, as we said, the promise is already resolved. Once you set a promise's state to either fulfilled or rejected, you can't set it again.
Now before a promise's resolve or reject function gets called, a promise is in a state known as pending. This means that you're waiting for information to come back, or you're waiting for your async computation to finish. In our case, while we're waiting for the weather data to come back, the promise would be considered pending. A promise is considered settled when it has been either fulfilled or rejected.
No matter which one you chose, you could say the promise has settled, meaning that it's no longer pending. In our case, this would be a settled promise that was indeed fulfilled because resolve is called right here. So these are just a couple of the benefits of promises. You don't have to worry about having callbacks called twice, you can provide multiple functions—one for success handling and one for error handling. It really is a fantastic utility!
Now that we've gone through a quick example of how promises work, going over just the very fundamentals, we'll to move on to something slightly more complex.