>> Let's start this demo by
taking a look at a synchronous example,
and then we're going to spin this into the asynchronous example.
So for our example in both cases,
what we're going to do is we're going to make a call out to
a little web service that does very simple things for us.
In this case, what it's going to do is it's going to delay.
It's basically a REST API version of a sleep call.
I personally don't like doing
demos that really depend on sleep calls.
You are going to notice, I'll do a little bit
of that in the other one.
But I generally try to avoid that.
It's because I don't think that they're necessarily real-world,
that whenever I look at that,
I'm like, "Oh, well, you want to improve performance?
Just take out the sleep call, problem solved."
Maybe that's just me, but yes.
So instead, I always like to call out to some other service.
This is just a simple way that we can do this.
So more than anything else,
it's a fancy sleep call,
just because of the fact that we're calling an endpoint.
You're going to notice to start with,
that we are pulling in the timeit library.
I'm a huge fan of the timeit library,
just because it gives us the ability real quick
and easy to see how long something takes.
So we're just going to set up our default timer,
and we'll stop and start a stopwatch effectively,
and then print out however long this took.
We're going to bring in the request library,
which we talked about in our prior Python videos.
This is by far,
the most common library for making HTTP/HTTPS calls.
The thing about the request library though,
is the fact that it is synchronous,
meaning that it's always going to stop execution.
If we want to do this asynchronously,
we are going to need to do something different,
maybe use a different library.
That's actually what I'm going to do in
my other demo is use a different library,
a library that's built for asynchronous programming.
Now, inside of here,
I'm just going to ignore the little prints and things like that,
just because all that that's doing is
just making the demo look a little prettier.
So we're just saying, "Hey,
we're starting this, stopping this, starting this, stopping this."
So I'm not going to focus on the prints.
But instead, I want to focus in on line 6 here,
which is where we're now going to make our get
called and we're going to go get the text here.
Now, you're going to notice that we've got
one parameter that's called delay.
Delay is going to be x number of seconds that we want to delay.
You'll notice that when we run the demo here,
that in our demo,
we're going to go ahead and make a two second delay call,
and then a three second delay call,
and then eventually print out however long it took.
Now, all of that's wrapped up inside of a neat little run demo.
It's wrapped up inside of main for reasons
we'll talk about in the next section,
and now we're just simply calling main.
So now, let's go ahead and bring this
together by saying python and then my little sync_demo.py.
What you're going to notice is that this
is performing the operation synchronously.
So it started the two second delay, and then finished.
It started the three second delay, and then finished.
You'll notice that the entire operation took 6.3 seconds.
Again, that extra 1.3 is just because of how long
it took to spin up and tear down the appropriate connections.
That's not necessarily a great way to program that.
If I'm working with something that's single user,
maybe I'm putting this inside of a Jupyter Notebook.
By the way, there might be another course where
we talk about Jupyter Notebooks, go check it out.
But if I'm putting this inside of a Jupyter Notebook,
it's just me, I'm not necessarily doing anything else,
I can't move without waiting for the value here,
then just go ahead and do this.
But if I am building something up that's going to be
multi-user or I want it to be better performant,
then I'm going to need a better answer.
So this is that next section that I'm talking about.
This is now where we're going to get in,
and start to work asynchronously.
So what I want to highlight up at the very top here,
is we're pulling in two little libraries here.
We're pulling in, first of all, aiohttp.
Now, the big thing that I want to highlight about
this is the fact that this is another library here.
So it's not the request library,
this is not built in with Python,
but rather, it's a different library.
Aiohttp is all about asyncio and making HTTP calls.
Now, the way that this does its magic
is it's all based around a session object.
Typically, what you'll do is you'll create
one session and keep reusing that over and over and over again.
This actually gives you a little bit of additional performance,
because of the fact that it's able to
reuse that connection if possible.
So it does a little bit of
connection pooling for us automatically.
In fact, we can see right here that we're going
to create that session one time.
Now, you are going to notice that if we're going to
use await inside of a with,
that we do need to mark that as async.
Now, why is an async important here?
Well, the reason that async is important is
because it's basically telling our runtime,
"Hey, there's going to be an await command in here."
The key to an await command working
is that my runtime needs to understand what that is.
My runtime needs to understand,
"Hey, we need to pause here.
That's why we have that async."
Now, having that on a method might make sense, because after all,
we're going to call await eventually on that method,
so maybe it makes sense there.
Then on top of that,
when the method ends,
it just simply ends at that point.
So we want to make sure that the runtime
knows that somewhere in here,
there's potentially going to be a blocker,
there's going to be an await.
With behaves in the exact same way.
That if you remember back to the prior module,
we saw a with.
The whole thing about with,
is that it's going to do automatic clean up for us.
Well, we don't want it to clean things
up before we're done using them.
So again, when we're going to have that await,
we're going to pause right here,
wait for something else to complete somewhere else.
As a result, we need to make sure that
the outside is aware of that fact.
That's what async is doing,
is it's letting everything know, "Hey,
inside of here, we're going to have a pause for something else,
don't leave without me."
That's basically it here.
So that's why we have that both on the with statement here,
and on the little function.
What you're also going to notice right here
is we're going to do the exact same thing with that load_data,
put it in a two and a three second delay.
But this time, we're going to pass in the session.
Then if we spin it on up to the top,
what I want you to notice is a similar syntax here,
where now we're going to go ahead and call that session get,
and then we're going to go ahead and grab the text from that.
Now, you're going to notice
that I've got my
await right here because calling out to get the text,
that's going to be an asynchronous operation.
So that's what's going on here.
That's the setup for that aiohttp.
So it's a little bit more work than we had
for our requests because of the fact that it's asynchronous.
Now what makes the asynchronous magic
happen in Python is this asyncio.
Now, asyncio is new to Python 3.4,
and it's had a lot of changes since then.
There's constant turn here.
So I do want to highlight the fact
that we're recording this at a point in time.
So the code that's here will only run on Python 3.7 or higher.
Just this example here.
So if you're running this on a prior version, you can do this.
It's just a different way to go about this.
I really like this implementation.
This feels much more natural,
especially as somebody who's worked
with async/await in other environments.
So this only going to work on Python 3.7 or higher.
So you're going to notice that we pull in this asyncio library.
Now, what you're going to notice with
asyncio is we're now going to create these tasks.
This is basically that process or that promise rather.
This is where I'm going to say, "Hey,
load_data, go do something, go do this.
Then when you're done, make sure,
promise me that you'll let me know that you're done.
Promise me that you'll go get that value,
and you'll give it back to me when I ask for it."
That's what's happening here.
So we're setting that up.
Then what we're going to do is we're going to say, "All right.
Now, we're ready for that value."
So now I'm going to say, "Hey,
give me the value back from the two_task.
Hey now, give me that value back from the three_task."
Now, I'm not printing them out in this case,
mostly just because it generates a lot of
noise on the screen because what the text gives me
back is the full JSON response from the endpoint.
It just gives me a lot of extraneous data.
So I'm just trying to keep things relatively streamlined here,
so I'm not going to worry about printing that out.
I'm just simulating grabbing the values here.
That's really the important part is
that ability to go ahead and grab the value.
Now, what's great about setting up
those tasks is the fact that I can do other work here.
So that other work might take a little bit of time,
so this is now where I'm going to do that sleep.
So now, I'm going to sleep for a second and then I'm
going to print out doing other work.
Then finally, at the very end,
we'll print out whatever the timer was.
When I run this code,
what you're going to notice,
let me just clear my screen here,
is it starts both timers,
it does the other work,
it finishes both timers.
Then the total elapsed time that it took was 3.3 seconds.
Again, that 0.3 is the build up and tear down of the HTTP calls.
But now, what I want you to notice is the fact that we
actually had three "long-running operations."
We have the one second sleep timer, and then the two,
and the three second delay that we were
calling out to that HTTP service,
and in the end,
it only took 3.3 seconds.
This is what async/await is all about.
It gives us this ability.
It gives us the ability too without
necessarily having to spin out
different threads and manage them and so forth,
be able to still take advantage of
a little bit of potentially multicore,
or just the ability for Python
now to be able to swap things on and off
because now we've given it a little bit of
a heads up of the fact that
certain operations are going to be long-running,
and it's okay if it does something else.
That is async/await inside of Python.
Now, I again want to stress the fact that
there's a lot that's going on here with async/ await,
and we are going to link to
more information inside of the GitHub page.
So if you want to keep digging into async/await,
you can absolutely do this.
I will say that in today's day and age,
understanding async/await is one of
those core concepts that you will want to become familiar with.
I also want to highlight this fact.
Asyncio in Python is still actively being developed,
things are going to change over time.
So the code that's here,
while it will likely continue to work in Python 3.7 and higher,
there's a very good chance how things work may change,
and there's also a very good chance that there might be cool
new fancy ways to do things as well in the future.
So again, we're recording this at a point in time.
Right now, this is the way that things behave,
things could always change in the future.
So all of that is how we can do async/await in Python.