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.