Introduction and Overview
Hello everybody. Kyle here from WebDev Simplified. In today's video, we're going to be talking about promises in JavaScript, which look incredibly difficult but are much more simple than they actually appear. So, let's get started now.
What a Promise Is (Conceptual)
To get started, let's talk about the idea of a promise before we get into the actual syntax of it. A promise in JavaScript is just like a promise in real life. What you do is you commit to something by saying, "I promise to do something." For example, "I promise to make the best video on promises I can." And then, that promise either has two results. Either that promise is completed, it is resolved, or that promise is failed and it is rejected. So, if I give you the best video ever on promises, then I will resolve my promise to do so. But, if I failed to give you the best video ever on promises, then that would be rejected because I was not able to complete that promise and I rejected it. So, now let's look at the actual syntax of creating a promise.
Creating a Promise: Syntax and Example
We just create a variable here. We'll call it P. And we're going to set it to a new Promise. And this Promise object is going to take one parameter, which is a function, which gets passed two variables of resolve and reject. So, we have a resolve parameter, a reject parameter, and then we need to actually create the definition of that function inside. And if you're confused by this arrow syntax here, just check out my video where I covered the arrow syntax for functions because it's extremely straightforward and much easier to write functions in this manner. So, after you're done checking that out, now inside of this promise section, we need to define what the actual promise is. So, in my example, the code inside of here would be me giving you the best video ever on promises. So, we're just going to put some simple code in here. We're going to create a variable A. And we're just going to set it to 1 + 1. So, that way this is what the promise does. And if this failed, we would reject it. So, we would say, "If A is equal to two, we would resolve it. But, if it is not, so let's say it's not equal to two, we'd reject." So, in the reject, we can pass anything that we want back into this reject. We're just going to pass the message that said, "Failed." And then in a resolve, this again, we can pass it absolutely anything we want. But, we'll just pass it a message that says, "Success." So, as you know, this code is always going to be successful because 1 + 1 is always equal to 2. So, it's going to call the resolve method that gets passed in. But, if we change this to be 1 + 2, we would get this reject method because it doesn't equal 2 anymore and we would be calling the reject method. So, now let's look at how we actually interact with promises. So, if we go down here, we can just say that P, since it's our promise, then all we have to do is say .then. Anything inside of a .then is going to run for a resolve because I say, "I'm going to give you the best video ever on promises, then you are going to do something else after I'm done with that." So, in here, this .then, all it does is take a method. In our case, it's going to just have a single parameter since we're passing a single parameter to resolve, and that's just going to be our message. And then we just wanted to find what we do with that function. So, we can just say console.log, "This is in the then." and then we can just pass it the message. So, we can actually see what that is saying. But, to be able to catch an error, we need to use the .catch version of this. So, we just say .catch, and this will catch any errors which are our reject states. And just like our then, we're just passing it a single parameter of a message. And we can just do something very similar. We would do console.log, "This is in the catch." and we can just print out that message. And there we go. This is exactly how to use promises. They're very similar to callbacks, which we're going to look at in a little bit, but a little bit cleaner way of doing callbacks. And as you can see, then is going to be called when our promise resolves successfully, and catch is going to be called if our promise is rejected or fails. So, now let's actually run this and see how it looks. So, now if we save that, you'll see that we got "This is in then," and it is a success because 1 + 1 is equal to 2. And if we change this to be not true, and we save it again, you'll see that "This is in the catch," and it has failed. So, as you can see, when we call this, we create a promise here, we tell it what we want to do when it succeeds, we tell it what we want to do when it fails, and then in our code we say, "Do this when it succeeds, and do this when it fails." Promises are really great when you need to do something that's going to take a long time in the background, such as downloading an image from a different server, and you just want to do something after it's complete, instead of making everything else wait for it. And then also, you can catch it to see if it's failed, so that way you can maybe retry it or give the user an error message if it did fail.
Replacing Callbacks with Promises
So, now let's look at an example of how we can take something that uses callbacks, which are what promises are meant to replace, and actually replace it with promises, and it's a lot easier than it sounds. I have open a very simple callback function, which takes two callbacks, one for the success, and one for an error, and all it does is check two variables to see if either of them are true. If they are, it'll throw an error, and if neither of these variables are true, it'll call the success callback saying that everything went well. So, this watch tutorial callback function, all we do is we call it, and we give it our two callbacks. Our first callback is going to be for successes, and our second callback is going to be for an error. So, if we save this and run it, as you can see, both our variables are false, and we get success, thumbs up, and subscribe. But, if we change one of these variables to true, let's say the user left while watching the tutorial, and now going to say user left with a frowny face, or if we change that back to false, and we change this one, say that the user is now watching cat memes to true. It'll say that the user is watching a cat meme and that cats are better than me. So, now let's implement this using promises instead of callbacks because this is what promises were really meant to solve. So, all we need to do is we can just copy this entire function here. We'll just paste it down here a little ways so we can have it to work with and we'll just change it to be promise instead of being callback for the naming. And we can completely remove both of these callback functions or parameters because that's the whole point of using promises is that we no longer have these callbacks. And all we need to do inside of here is return a promise. So, we can say return new Promise and as we know that Promise takes two parameters, the resolve and reject. And inside of that function, we just want to define all of our code that was calling these callbacks. So, resolve is going to be our successful callback. So, we can just replace everywhere where we have callback with resolve. And reject is going to be that error callback. So, we'll just replace all of those. And there we go. We've completely overhauled this function to use promises instead of callbacks. And as you can see, the code itself is almost exactly the same. All we did is change a few variable names and now we're returning a new Promise instead of calling the callbacks. So, now let's look at how we can actually use this function. So, let's copy what we have up here so that we can see how we need to change this watch tutorial callback into watch tutorial promise. So, the first thing that we know is that this is a function that takes no parameters. So, we need to call this function and then do something afterwards since it returns a promise. So, we say .then and then, like we know, is going to be our success callback. So, we can make it our very first method here, which is this. So, as soon as that function is done, we just need to end that and now we can do our .catch. So, we just add .catch in here to catch all of our errors. And there we go, we've completely transformed that callback to be using promises now. And as you see, again, our code is almost exactly the same. So, now if I comment out all of this callback related stuff, and if we rerun it, you'll see that we get the exact same output for no matter what set of variables we have. Let's say we change it all to false, you see we get success. Change this one to true. And again, you'll see user left. And this is using promises now instead of using callbacks. And as you can see, the code is a lot cleaner to write than with using callbacks because as you start nesting callbacks, you start to get in a huge world of trouble where your code just keeps getting indented and indented even further. But with promises, instead of nesting callbacks, all you do is just add another then. So, it would look just like this. You would have then and then instead of having a callback inside of a callback inside of a callback, which is what's known as callback hell, and it's absolutely terrible. Promises are great and they completely solve that problem for us.
Composing Promises: Promise.all and Promise.race
Now that we have an understanding of how promises work, let's take a look at some of the things that we can do with promises. I've changed my code a little bit here so that now we just have three simple promises being created. And they're super simple. All they do is always resolve, they never reject, and they just send a single message when they resolve. And each one of these is about recording a new video for my channel. So, let's say we want to do something after I recorded all three of these videos. And we want to record run all of these in parallel at the same time so we don't have to worry about waiting for one before we start the next. We can use what's called Promise.all. So, we just say Promise.all. And inside of here, we send it an array of all the different promises that we want to run. So, in our case, we want to record video one, we want to record video two, and we want to record video three as well. And Promise.all is going to run every single one of these promises, and as soon as it's done, it is then going to call the .then and .catch methods depending on if they resolved or failed. So, in our case, all of these are going to resolve, so we'll use a .then. And this .then is going to send an array of all of the successful messages. So, it's going to send us an array with all of these different result parameters. So, we're going to have a messages in here. And this messages is just going to be an array that we're just going to print to the screen. So, we'll print messages. And if we save that, you'll see that we got "video one recorded, video two recorded, and video three recorded" in this array here, which is exactly perfect. That means all of our promises ran, and they all returned. And as soon as they were all done, it called this .then method. And you can't really see that with this example, but these are all running at the exact same time. So, if this one, for example, took a long time and actually needed to call the database, for example, and get some results back, it'll run at the same time as these other two. So, if these other two are very quick, they won't have to wait for this first one to finish. And now, let's say that we want to just do something as soon as one of my videos is done and being completed, we can use Promise.race instead of Promise.all. And Promise.race is just like Promise.all, except for it'll return as soon as the first one is completed instead of waiting for everything to complete. And because of that, it'll only return a single message in the .then as opposed to all of the messages. So, now if we run Promise.race, you'll notice we're only going to get one return value, which is "video one recorded." And that's as we expected. Video one was the first one to record, and that's just because these are so simple, it's going to run them in the same order.
Wrap-up and Next Steps
And that's all there is to creating promises in JavaScript. They're extremely straightforward once you understand the concepts of resolve versus reject, and very similar to callbacks, which you probably already know from using JavaScript before. So, if you enjoyed this video, please make sure to subscribe to my channel for more videos like this, and check out my other ES6 JavaScript related videos over here. Thank you guys very much for watching, and have a nice day.