The Evolution of Async JavaScript: From Callbacks, to Promises, to Async/Await

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
This is one of my favorite websites because there's probably not many other websites out there that are as old as this website and that have had as few bugs as this website has probably had. And the reason for that is because this website is really just mostly static. In fact, it's probably entirely static. It turns out it's pretty simple to build a site if you have all of your data or all of your content upfront. Unfortunately, most of the sites we build today, that's not the case. So what we've done is we've invented these patterns for handling and fetching external data. So what we're going to do in this video is we are going to break down the three most common patterns for fetching external data into our applications. They are callbacks, promises, and async await. So let's start off talking about callbacks. But before we actually do that, we need to make sure that you have a solid foundation and understanding what a function is, right? So when I was first starting out programming, the way that I conceptualized functions or what helped me conceptualize functions is to think of a function as a physical machine. So here we had a add machine, it took in two inputs and then it returned a value. So just like we have like a washing machine or a toaster or a dryer, all these physical machines that we have in our lives, I would think about functions similar to how I would think about those machines as well. So if we wanted to start the machine, the way I would think about it is we just need to press the button on the machine which as you know is just two parens and that will go ahead and invoke the function. But what's important to understand about these machines again is that anybody can walk up to the machine and they can start the machine. So the way I thought about this was if I wanted to start the machine, I could do that by doing something like this. So in JavaScript, we know that what we're doing here is we are just creating a brand new reference to this same function but when we do that, that allows me to also invoke the original add function as well. And if you wanted to press the button on the machine, you can do that as well. So it doesn't matter in JavaScript who invokes the function or who presses the button on the machine. When the button is pressed, the machine is going to run. And what this shows us in a more technical term is that we can create as many references to the original function as we want and when we invoke those references, it's as if we are invoking the original function. So let's take this a step further. What if we came in here and we created a new function? So it's as if we are creating a brand new machine to go along with our analogy and this function is now going to take in two values as well. It's going to take in x but it's also going to take in a reference to the initial add function. So when this addFive is invoked, what it's going to look like is we're going to pass in some number and then we are going to pass in a reference to the initial add function. Again, it doesn't matter here if this reference is something like this. We can do this as well. All of these are just referencing the same spot in memory. They're referencing the exact same function. So what we'll do here, so let's change this back. So now we know that addFive is taking in a number and it's taking in a reference to our initial add function up here. So what we will do is we want to return and again, now that we have a reference to add, anybody can press the button on the machine to make it run. So what we're going to do is we know we want to add five because that's the name of the function so we're going to pass in five and then we're going to pass in the initial value that was passed in when addFive was invoked which was 10, which was x, right? So here what we're doing is we're showing how you can pass a function definition or a reference to a function into another function and then inside of this function, we can go ahead and invoke the initial function. Now your brain might have gotten a little weird there but just remember, back when we had code that looked like this, you were cool. When we had something that was like just a reference to add, no problem here. But when you start passing functions to other functions, for some reason, your brain just gets a little bit weird if you're not used to it. So really again, all we're doing here is just as we created, that should be you, just as we created a reference here to our initial add function, what we're doing as well, when we pass in add is pretend like we're just creating another reference that we can invoke. So these concepts have a name. So I'm going to create another...let's just go ahead and run this actually so you can see it. When we run addFive, you'll notice that we get 15. And as I mentioned, these functions have a name and I'm just going to create a new function here that highlights what their name is. So when you have a function that receives another function as its argument, that function is called a higherOrderFunction. And the argument that you're passing in to that function is going to be called a callback. So we can do something like this and it's the exact same as this function right here. So we're saying go ahead and call the callback passing in 5 and whatever the x value is. So now when we invoke higherOrderFunction, we can give it a value and then we want to give it a callback function which is going to be our initial add function. Again, so to recap, any function that receives another function as its argument is called a higherOrderFunction and the function that you're passing in as the argument is called a callback function. So now we can run higherOrderFunction and we'll get 15. Now regardless of if you were aware of the naming of higherOrderFunctions and callbacks, odds are you've used them before because they are super popular in the JavaScript space. So here are three examples. So if you've ever used an array method like map, you have used a callback before. So here is the function that we are passing to another function. So this is our callback function and map would then be technically a higherOrderFunction. If you've ever used a library like low dash, same thing. We pass low dash as filter method and array and then we pass it a callback function. And if you've ever used something like jQuery, callbacks are all over. So we're saying whenever there is an element with an ID of button, whenever that gets clicked, go ahead and run these callback function right here. So there's really two ways that you can think about the usage of callback functions. The first two as we're seeing up here, they're kind of used to create an abstraction over turning one value into another value. So here, we are turning this array into a new array with all of the elements getting plus five added to them. And then here, we are turning one array into another array after we've filtered out some items. So these are examples of kind of again using that abstraction layer to get or to turn one value into another value. But the other way that callbacks are used is kind of like this jQuery example. What this shows is that with callbacks, we can delay execution of a function until a particular time. So here, what we're doing is we're saying, "Don't call this function until the element with an ID of button is clicked on." So we're delaying the execution of this function until some particular event happens later on. Now the purpose of this video is to show you different asynchronous patterns in JavaScript but up until this point, we really haven't even talked about anything asynchronous yet. But I want you to think about the definition that I just gave you or one of the benefits of a callback and being that it allows you to delay execution of a function until a particular time later on. That sounds like a great use case for fetching external data because initially, we're not going to have that data. So what we can do is we can say, "Hey, go ahead. Fetch the data." And then later on once that data comes back and we have the data, go ahead and execute this function. So a really popular example that we can see is if you've used jQuery before, you have done something like this where we have this getJSON invocation. So this is just an Ajax request. We are hitting some endpoint, in this case, it's the GitHub API and updateUI and showError, we're just going to assume those exist. They're not really important. But what we're doing here is we're demonstrating how we can use callbacks to make an asynchronous request. So we're saying, "Hey, go ahead and make an Ajax request to this URL." And when the data comes back, then go ahead and invoke the updateUI function passing it that data. But if there's an error, go ahead and invoke showError. So the invocation of these functions are being delayed until a later point in time which is either when we have the data or when there is an error. So now we've seen how callbacks are helpful for making asynchronous requests but let's go ahead and take a look at an example of where callbacks can get a little bit messy and some of the downsides of using callbacks. So here what I have is just a simple app. When we click on this button, what happens is we go and we fetch data from the GitHub API. We then get that user's location and we then fetch some more data from the Yahoo weather API which then gives us the user's GitHub information as well as some information about the current weather, where they live. So here what's happening is you'll notice that we have added a few layers of callbacks around our initial callback. So we have this jQuery code up here where we're saying, "Go ahead and run this function whenever the element with an ID of button was clicked on. When that happens, make our first Ajax request." And this is just what we saw earlier where we hit the GitHub API. And then once that is a success, we have the user, go ahead and make another Ajax request to and this just getLocationURL gets us this really long gross query string here that hits the Yahoo weather API. And then when that's a success, we go ahead and we call updateUI which just takes the user and the weather and adds them to the view here. So what we're seeing here is what's called callback hell. That is a term that has been coined before me. That is not my term. But the idea here is that this logic is kind of hard to understand especially if you're not used to it. The way our brains think is we think sequentially naturally. And what this is doing is it's kind of forcing us out of that natural way of thinking because we have to go here and then we go here and we say, "Okay, if this is successful, do this, and if that's successful, then do this but if there's errors, then come down here and do this." It's just really hard to follow and we start getting this pyramided doom shape right here where all of these invocations especially these asynchronous invocations get nested inside of each other and it's kind of just hard to follow both as the creator of this this code right here. But also you as the viewer reading this, it's probably hard to understand because there's just so much async stuff going on and it's not really a sequential way of thinking about things. Some advice that people give for managing this issue of callback hell is to break your code out into functions and kind of modularize your code. So we can show that here. Let's create a getUser function. Let's have it take in an ID. We'll say onSuccess and onFailure. These are going to be callback functions. And then what we're going to do is I'm going to copy these two lines right here, so we'll paste those in and then success is going to run our onSuccess callback function and then error is going to run onFailure and then we'll go ahead and close this. That looks good. And then we will do the exact same thing for our getWeather function taking user onSuccess as well as onFailure and then let's copy this line right here. So we'll say that getWeather is going to make a Ajax request and then if it's successful, we'll call onSuccess and if it's not, then we will call onFailure. So now we need to refactor this code down here. So what we'll do first, I'm just going to delete all this because it'll be easier, is we want to call getUser passing it. First, the user ID. Second, the onSuccess callback function. We'll just do that for now. And then third, the onFailure callback function or if you remember, we have just a showError function that's defined up here. Just console.logs or console warns the error. All right. So that looks good. Now when this callback function is invoked, it's going to be passed the particular user. So then from there, we can call getWeather. We're just going to take in the user, the onSuccess callback, and then the error callback, and then from here, we're going to be passed the weather which we can then finally come in here and call updateUI passing it the user as well as the weather which we will say weather.query.results. And so now if we don't have a typo, this should be the exact same. So everything looks to be working well but we have modularized our code a little bit and we have broken out all of this logic into their own functions. So this is better because we have these descriptive function names. It's a lot easier to understand what's going on but the fundamental problem is still here. We still are having to think in this asynchronous non-sequential manner when naturally, we're not used to thinking about that. So we still have to get the user and then we have the user here, then we have to call getWeather. It's just a weird way of thinking. So even though we've improved our code and made it more modular, the fundamental issue of callbacks still exists. Now the next biggest issue that callbacks face has to do with inversion of control. When you write a callback, you're assuming that the program you give your callback to is going to invoke it correctly and they're going to pass the correct arguments to it and they're only going to invoke it when they say they will. So say we had a critical function here and this critical function can be doing something like charging customers, tracking analytics, really anything that is important. Now let's say we were using some third-party library, right? This could be Google Analytics, it could be Stripe, it could really be anything. The way that we interface with a lot of third-party libraries is we pass them our callback functions. So here we have a higherOrderFunction and here we have a callback function. We are assuming that this third-party library is going to invoke our function in a responsible manner, just how they said they would with the correct arguments. But the problem is because we aren't the ones invoking critical function, we have zero control over that. They may, whether it's accidental or not, change their API and now all of a sudden, instead of calling criticalFunction just once, they're going to call criticalFunction every time a specific event happens which can completely break your app or they're not going to call criticalFunction at all. There's so many use cases where passing a function to a third-party library or a third-party service can go wrong because it's entirely up to that third-party service to invoke the function. You have zero control. So this idea of inversion of control is that when you use a callback as the API for interacting with a third-party service, you're essentially inverting the control to them. So you're losing all control of the function itself and you're giving all of that control to the third-party service. So now that you understand, one, what callbacks are used for, how they can be used asynchronously because we can delay execution of a callback until a later point in time, you also understand kind of the downsides of a callback. We have callback hell, it forces us to think non-sequentially, and we have this inversion of control problem that we just barely talked about. The next pattern we're going to look at solves some of these issues that we see with callbacks and they're called promises. Now before we actually dive into the code for promises, I want to talk about a problem that I've been seeing lately. Have you ever been to a busy restaurant? Odds are you have. What happens is if there's not a table available for you immediately, what they usually do is they take your name and then whenever a table opens up, they yell your name and then you go and sit down at your table. But what restaurants have been doing lately is instead of taking your name, they'll take your phone number. That way, you can continue shopping, you can continue doing kind of whatever you were doing before, and when a table opens up, they text you. Now that seems really nice but what's been happening is you assume when you give your phone number to a restaurant that they're only going to text you when a table opens up but because they have your phone number, what they can do is they can send you things like ads. Let's say on a Tuesday, they're not busy, they can say, "Hey, 20% off for the next hour," because there's not a lot of people here. And now this should sound familiar because it's kind of exactly what we just talked about. Giving your number to a restaurant is just like giving a callback function to a third party service. You expect the restaurant to just text you when a table opens up just like you expect the third party service to invoke your function when and how they said they would. But once your number or the callback function is in the hands of the third party service or the restaurant, you've kinda lost all control. Thankfully, there is another solution that exists and by design, it allows you to keep all the control rather than giving up control to the restaurant or the third party service. Now you've probably seen these before, these little restaurant buzzer thingies. The way it works is instead of you giving your number to the restaurant, the restaurant hands you this little buzzer thing. And then whenever it buzzes and it starts lighting up, that means your table is ready. So you get the benefit of being able to do whatever you want. You can go shopping. You can just sit at the restaurant. It's entirely up to you. So you get the benefits of freedom while you wait for the table to open up but without the downsides of actually giving up anything. And in fact, they actually give you something. So there is no inversion of control you can think of it. Now this buzzer has three different status to it. It can be in one of three different states at a time. Pending is the default state. It's the initial state. So when they give you the buzzer, it's in the pending state. Fulfilled is when the state of the buzzer is flashing and your table is ready. So when the status goes from pending to fulfilled, you know that your table is ready and you can go eat. Rejected is the state the buzzer is in when something goes wrong. So maybe the restaurant is about to close so that they have no more tables left or someone rented out the restaurant and they forgot. So the buzzer again can always be in one of these three states. Again, the important thing to remember here is that you, the receiver of the button, you have all the control. If the buzzer gets put into fulfilled, you can go to your table. If it gets put into fulfilled and you want to ignore it, you can do that too. If it gets put into rejected, that kind of sucks but you can go somewhere else to eat. And if nothing ever happens, if it just stays in pending, you never get to eat but you never actually out anything because you never gave up anything. All right. Why am I telling you this? Why are we talking about the restaurant buzzer thingies? Well, it turns out that if giving the restaurant your number is like giving them a callback function, then receiving the little buzzy thing is like receiving what's called a promise. So why do promises exist? Promises exist to make the complexity of making asynchronous requests more manageable. Exactly like the buzzer, a promise can be in one of three states: pending, fulfilled, or rejected. But unlike the buzzer, instead of these states representing the status of a table at a restaurant, they represent the status of an asynchronous request. When you make an asynchronous request with a promise, the default status is going to be pending. So as the asynchronous request is still happening, the status of that promise is going to be pending. If the asynchronous request was successful, the status is going to be changed to fulfilled. And if the async request fails, then the status is going to be changed to rejected. So now we need to answer three questions about promises really. They are, how do you create a promise, how do you change the status of a promise, and then how do you listen for when the status of a promise changes? So the first one is easy. How do you create a promise? I'm gonna put these in a comment so we don't get an error. So the way that you create any promise is just by creating a new instance of the promise constructor just like that. So now the question becomes, how do you change the status of a promise? When you invoke the promise constructor, this constructor is going to take in a single callback function ironically perhaps and this function is going to be passed two arguments. It is going to be passed a callback function called resolve and it's going to be passed another called reject. Resolve, when it is invoked, it's going to change the status of the promise to fulfilled and when reject is invoked, it's going to change the status of the promise to rejected. So if in here we call resolve, again, that's going to change the status of the promise to fulfilled. And if we call reject, that will change the status of the promise to rejected. So now the third question we need to answer is how do we listen for when the status of a promise changes. Now in my opinion, this is the most important question that we need to answer so far because it's cool we know how to create a promise and it's cool we know how to change that promise of status but that's kind of worthless if we don't know how to do anything else after the status changes. So one thing we haven't really talked about yet is what a promise actually is. So let's go ahead and just console.log(promise) so we can see this. You'll notice that promise is really just an object with a bunch of fancy things on it and on the prototype of the promise constructor, there are a few different methods that we can use. We're going to focus on then as well as catch. So to answer the question specifically, when the status of a promise changes to fulfilled, which it will when we call resolve, the function that you pass to .then is going to get invoked. And when the status of the promise changes to rejected which it will when we call reject, the function that you pass to .catch is going to be invoked. So we can see this in action. That was a lot of words but let's go ahead and create a few different functions here. Let's just console.log('Success!') and then we will have another function here called onError. And then now, let's go ahead and create our promise. So we'll create a new instance of promise, pass it a function which will be passed, resolve and reject, and then let's just say after two seconds, we want to go ahead and call resolve which will change the status of the promise to fulfilled. And so the way we do this is, again, promise is an object with access to a .then property. So we pass on success to .then and then we will come down here and we will say catch and we will pass to it onError. So when we run this, what we expect to happen is we create a promise, after two seconds, this promise is going to get fulfilled. So the status of the promise is going to change to fulfilled which will then call the function that we pass to .then. So we should see success in the console. I'm going to change this to var just so we can run this a few times. So it's pending. Two seconds later, we get success because the status of the promise changed to fulfilled. And then now we can see this again. If instead of calling resolve, we call reject, that's going to change the status of the promise to rejected which will then call the function that we pass to .catch which is going to be our onError function. So we can see this as well. So after two seconds, what should happen is we see our nice little poop emoji there in the console coming from our onError function. So now that you know your way around the promise API a little bit now, let's go ahead and head back to the code that we saw earlier and we can see how promises make confusing callback code a little easier to understand. So what we'll do is we want to now utilize promises inside of our code right here. So where we will start is with our getUser and our getWeather functions. Instead of taking in callback functions, what we're going to do is we're going to return a promise instead so we can use .then after we invoke getUser as well as getWeather. So what we'll do is let's create a brand new promise inside of getUser. And notice we are going to return it. And so then let's take our Ajax request inside the promise. We have access to resolve and reject. And so what we're going to do is we're no longer going to take in any callback functions up here. So we can get rid of those. And then when there is a success, we want to call resolve and when there is an error, we want to call reject. So that makes sense. Let's go ahead and do the exact same thing down here in getWeather. So we move our Ajax request inside of the promise. We now are no longer going to take in an onSuccess and onFailure callback. Instead, we, just as we saw earlier, will call resolve and reject. So nothing too fancy going on here yet. Let's go down now and update the invocation of these functions now that they are returning us a promise. So what we will do is let's say userPromise is going to be whatever the invocation of getUser returns us which we know is going to be a promise. And then from there, we want to call .then. When this promise resolves, it's going to give us the user because that's what the Ajax request is going to get us and then it's going to resolve with whatever it gets us which is the actual user. From there, we want to create a weatherPromise which we can do by invoking getWeather, passing it the user, and then from there, what we can do is we can call weatherPromise.then which is going to resolve with the weather. Once we have the weather as well as the user, we can go ahead and call our updateUI function passing it the user and then passing it weather as weather.query.results and then now everything is almost exactly the same. We just need to invoke showError if there was any errors. So what we will do here is right here, we will call weatherPromise.catch passing it showError. And then down here, we can call userPromise.catch passing it showError as well. So what we've done is we've refactored this code to instead of using callbacks, we are now using the promise API. And so if this is all working, everything will function the exact same. But now what's nice about this is instead of having to think in terms of callbacks, we can think a little bit more sequentially with promises. So we can say go ahead and get the user and then get the weather and then update the UI. So at this point, you might not be entirely convinced that promises are anything but marginally better than callbacks but let's see another feature of promises that will make our code here a little bit better. So inside the console, I'm going to paste in this code right here. So we have a getPromise function that just returns us a new promise that's going to resolve after two seconds and then we have log ( 'A' ), log ( 'B' ), logCAndThrow. So this is going to log the letter C and then throw a new error and then we have a catchError. Whenever you invoke .then or .catch, it's going to take whatever you're returning and it's going to wrap it in a promise. Now, that might not seem like a big deal but what that allows us to do is called chaining. So we get the promise that's going to obviously return us a promise it's going to resolve in two seconds. Because it returns us a promise, we can call .then on it. So we can say after two seconds, go ahead and logA. Now because again as I just mentioned, anytime you call .then, it's going to take whatever you're returning and wrap it in a new promise. So even though logA only console.logs('A'), because it's being a wrapped in a promise, that allows us to chain another function, so we'll say logB. And because that's going to be wrapped in a new promise, we can come in here and we can say logCAndThrow. And because that's going to be wrapped in a new promise, we can have .catch on here which is going to catch the error that is being thrown which will then run our catchError function which will log ('Error!'). So if this works correctly, what we should see is after two seconds, we get A, B, C, and then we get an error because of this idea of chaining where any time then or catch is invoked, it's going to wrap whatever these functions return in a new promise which obviously have a .then and a .catch property. So another common example of this is if you've ever used fetch before, it's another way to make Ajax requests. So let's say we were hitting like our endpoint here, fetch is going to return us a promise that is going to resolve with the HTTP request itself. So what we can do is we can grab this response, call .json on it, and because that's going to wrap it in a promise inside of here, we have access to user and user is ready to go. It's the JSON. So this is just a very common use case for when you would see chaining promises in the wild. So now that we know about chaining, we can come back to our code here and we can refactor it a little bit. So instead of creating a user promise here, let's just go like this. Where we invoke getUser, that's going to return us a promise that we can call .then on. And then we can invoke getWeather which is going to turn us another promise and then we will have access to the weather. And then down here because we know that's going to return us a promise as well, we can have .catch with our showError callback. So now we can delete all of this code. So this looks much better but you'll notice here, or you may notice, it's a small detail but we're going to run into an issue. If you remember, when we called updateUI in here, we needed access to both the user as well as the weather. But right now, the way we currently have it set up is we don't have access to user because user or getUser was invoked up here and we're not passing user down the chain. Instead, we're just using it right here to get the user's location and then we are resolving with whatever this Ajax invocation request gets us which is just the weather. So this brings us to the last detail about promises that I want to cover and that is how you can pass data into whatever the .then callback is by passing it as an argument to resolve. So what I mean by that is again, what we want to do is we wanna be able to access user inside of this function right here. So what we can do is we're going to go ahead and invoke resolve ourself and anything that we pass to resolve will be available to us in the function that we're passing to .then. So we can come in here and we can pass along user. Then we can say weather is going to be weather.query.results. And then now instead of this being weather, this is really going to be this data object right here which we can then pass to updateUI. So we can go ahead and put this just on one line for now. So that looks good. So now if we call getData, we have an error because this needs to accept the weather. And there we go. Everything is the exact same as it was but now look at this. We've gone from...let me paste this in. We've gone from code that used to look like this which is pretty hard to follow to code that now looks like this. It's more of this natural sequential way of thinking. We get the user and then we get some weather and then we update the UI and if there's an error in there, we invoke showError. So again, that's the callback version and this is the promises version. So at this point, it's pretty clear the benefits of promises over using callbacks just in the way that it helps us think kind of more naturally. But I want you to try to think of if there's any way that we can improve this code right here and I don't want you to be held back by what you think the language limitations are. Let's pretend we are on TC39, the JavaScript or the ECMAScript Standards Committee and we have full control over adding any feature to the language we want. One of the downsides of promises that we kind of ran into here was if we needed the value that we got from this promise which was the user, if we needed that lower down in the chain, we needed to pass that down as a value. And not only that, whenever you add a new API, it's just something else you have to remember. So this is kind of a smooth...it's a clean API. I like it, .then, .catch. It's intuitive. It works out well. But it would even be better if we just didn't have to do that, right? What if we were just able to do something like this where we just invoke getUser as if it were a normal function and then we can come and do the exact same thing with getWeather and then we can just pass it the value of user? We don't have to worry about passing it down the then chain and then we can get rid of this and we would just have to do updateUI, passing it an object with user, and then the weather. This is essentially like the holy grail of asynchronous code. What we're able to do or what we would like to be able to do is invoke a asynchronous function that returns us a promise as if it were synchronous and then the JavaScript engine would be smart enough to realize that this is returning a promise so we should wait a little bit instead of continuing on, wait until this promise is resolved, get the specific value, save it into the variable, and then continue on. As is, this may be kind of hard to add to the language because the JavaScript engine would have to be really smart and on the fly, it would have to figure out if the specific function invocation is really of any function invocation is returning a promise or not. So what we can do because we are in charge of the language specification, let's go ahead and give the engine a little help here. So let's use this async keyword on the main function body here. So when the JavaScript engine sees this, it'll just know, hey, inside of this function, we are going to have some asynchronous function invocations. So now the only question the JavaScript engine needs to answer whenever it sees a function invocation is it needs to figure out if that invocation is going to return a promise or not because if it does, it should wait for the promise to resolve so it gets the value. So what we'll do here is let's just say like await. So when the engine sees that we are inside of an asynchronous function and when it sees the await keyword in front of a function invocation, it should know, hey, this function invocation is going to return us a promise. So instead of continuing on line by line as it usually does, the JavaScript engine will wait right here until the promise is resolved and then save that value inside the variable itself. So what's cool about this is as you might have guessed, this is actually already part of the language and it's called async await. So the benefits here are that we can write our asynchronous code as if it were synchronous and obviously the benefits of that are there's no API we really need to learn besides these two keywords right here. And this is how we're already used to programming. We're already used to programming in the synchronous fashion. So along the lines of async await, there are just a few other features of it that I want to talk about real quick. So whenever you have an async function, that function is always, no matter what, going to return you a promise. So here we have an async function called getPromise and then if we do this, you'll notice that it returns us a promise. Again, whatever you return, even if you have a return value, it's going to wrap that return value in a function. So that's the first thing to remember about async await is whenever you have an async function, it always returns a promise. We can even see that here. Let's say we had just a regular add function which is going to return us the value of adding x and y together. If you wanted to invoke this and then get the value, you would have to call .then because as I just mentioned, any time that you have async on a function, it's always going to wrap the return value inside of a promise. So we can get the result of that promise by calling .then. And then the next thing you need to keep in mind is if you try to use await inside of a function that's not async, you're going to get an error and this is going to say this is a syntax error and this is a syntax error. And this is kind of just mostly when you forget about it. So you need to make sure that you always, whenever you're using await, you always use async. And here's kind of how I think about it. When you add async to a function, it really just does two things and both of these we've already talked about. One, it makes it so the function itself returns or wraps what gets returned inside of a promise. And it makes it so you can use await inside of that function. Now the last thing I want to talk about is error handling with async await. You'll notice that when we were factored to this code right here, we kind of got rid of our error handling. We aren't calling our showError function anywhere anymore. So there's two common patterns for this. The first one is to wrap all of your await code inside of a try-catch block. So we can wrap all this or put all this inside of try and then if there is an error, we can go ahead and catch that and then invoke our error handler function. Another approach you can take and this won't work on our example here but let's say we had another async function here. We'll just call it add and pretend it's async. What you can do is when you invoke add, that's going to return you a promise again because all async functions return you a promise. So then you can just add .catch onto that invocation. But because in our example right here, we aren't the ones actually invoking this function because we're passing it to jQuery and it's in jQuery land, we can't actually do that but it is a good use case or it is a good way to catch any errors without having to wrap all of your code in a try-catch block if you are the one invoking the actual function.
Info
Channel: uidotdev
Views: 88,221
Rating: 4.9482055 out of 5
Keywords: javascript, callbacks, async javascript, async/await, async, await, async await, async await javascript tutorial, javascript programming language for beginners, asynchronous javascript, javascript async, tyler mcginnis, tylermcginnis, ui.dev, ui, async await javascript, learn javascript, learn javascript async, es6 promises, es6, javascript promises tutorial, js promises, javascript promises, javascript callback functions, javascript callbacks, javascript callback vs promise
Id: gB-OmN1egV8
Channel Id: undefined
Length: 45min 24sec (2724 seconds)
Published: Tue Oct 23 2018
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.