---
title: 'Next-Level Concurrent Programming In Python With Asyncio'
source: 'https://youtube.com/watch?v=GpqAQxH1Afc'
video_id: 'GpqAQxH1Afc'
date: 2026-06-17
duration_sec: 0
---

# Next-Level Concurrent Programming In Python With Asyncio

> Source: [Next-Level Concurrent Programming In Python With Asyncio](https://youtube.com/watch?v=GpqAQxH1Afc)

## Summary

This video explains how to use Python's `asyncio` package for concurrent programming, covering the difference between concurrency and parallelism, the `async`/`await` syntax, and practical techniques like `asyncio.gather` and `asyncio.to_thread` to convert blocking functions. It demonstrates significant performance gains by batching API calls concurrently.

### Key Points

- **Why concurrency matters** [0:00] — Modern software often waits for APIs, databases, or files; concurrency prevents wasted time by switching tasks during waits.
- **Concurrency vs parallelism** [1:10] — Parallelism runs tasks simultaneously on multiple cores; concurrency makes progress on multiple tasks by interleaving, even on a single core.
- **Why concurrency is smart** [3:06] — Many tasks involve waiting (network, I/O); concurrency lets the computer do other work during those waits, improving efficiency.
- **async/await basics** [3:51] — `async` marks a function as concurrent; `await` pauses execution until the awaited task completes, allowing other tasks to run.
- **asyncio.gather for batching** [6:39] — Using `asyncio.gather` to run multiple HTTP requests concurrently reduced total time from 2.0s to 0.2s (10x speedup).
- **Async generators and comprehensions** [9:50] — Python supports `async for` in generators and list comprehensions, but they still run sequentially unless combined with `gather`.
- **Converting blocking code with asyncio.to_thread** [11:56] — Use `asyncio.to_thread` to run a synchronous function in a separate thread, making it usable in async code without modifying the original function.
- **Alternative: aiohttp package** [16:53] — The `aiohttp` library provides native async HTTP requests, but the presenter prefers `asyncio.to_thread` for simplicity.
- **Impact on design patterns** [17:50] — Async/await integrates cleanly with patterns like Strategy and Factory; architectural changes may be needed for data pipelines.

### Conclusion

Concurrent programming with `asyncio` can dramatically improve I/O-bound Python applications. The `async`/`await` syntax and tools like `gather` and `to_thread` make it easy to implement, with minimal impact on design patterns.

## Transcript

modern software regularly interacts with
an api a database or a file that means
there's a lot of waiting and you need to
make sure that your software handles
that efficiently if you don't your
application is going to be much slower
and the more data you process the more
you interact with apis the worse this is
going to get the way to fix this is to
rely on concurrency in python you use
the async io package for that i'll give
you a brief overview of how the package
works but then i'd like to go a bit
deeper and also show you how to turn a
regular blocking function into something
you can run concurrently which could
make your program a lot more efficient i
don't even have to modify the original
function for this it's really easy i'll
also talk about how concurrency affects
your software design and architecture so
make sure to watch this video till the
end if you want to learn more about how
to design a piece of software from
scratch i have a free guide for you you
can get this at ironcodes.com
design guide contains the seven steps i
take when i design new software
hopefully it helps you avoid some of the
mistakes i made in the past
ironcodes.com
design guide and the link is also in the
description of this video you may have
heard the terms concurrent and parallel
computing before but what's the
difference true parallel computing means
that an application runs multiple tasks
at the same time where each task runs on
a separate processing unit
concurrency means that an application is
making progress on more than one task at
the same time but may switch between
these tasks instead of actually running
them in parallel if an application works
say on tasks a and b
it doesn't have to finish a before
starting b it can do a little bit of a
then switch to doing a little bit of b
back again to a and so on this answer on
stack overflow nicely illustrates the
difference concurrency is two lines of
customers ordering from a single cashier
and lines take turns ordering
parallelism is two lines of customers
ordering from two cashiers each line
gets its own this year if you translate
this back to computers each cashier is a
processing unit a cpu core each customer
is a task that the processor needs to
take care of modern computers use a
combination of parallelism and
concurrency your cpu might have two four
eight or more cores that can perform
tasks in parallel your os will run tens
to hundreds of different tasks
concurrently a subset of those tasks are
actually running in parallel while the
os seamlessly switches between the tasks
parallelism in python has caveat which
is the global interpreter lock anytime
you run python code it needs to acquire
a lock on the interpreter there are
reasons for this that i won't go into in
this video but effectively means that
python code is single threaded even if
you stop multiple threads there are ways
around this for example by relying on
multiple processes instead of multiple
threads or by switching to an
interpreter that doesn't have to lock
this concerns parallelism though
concurrency on the other hand works
really well in python especially since
version 3.10.
why is concurrency a smart way to do
computing
well it so happens that many tasks
involve waiting or applications are
waiting for files to be read or written
too they're constantly communicating
with other services over the internet or
they're waiting for you to input your
password or click a few buttons to help
identify traffic lights and recaptcha i
hate those things it considerably speeds
things up if a computer can do something
else while waiting for that network
response or for you to finish cursing
about recaptchas in other words
concurrency is a crucial mechanism for
making our computers work efficiently in
this age of connectivity the async io
package in python gives you the tools to
control how concurrency is handled
within your application
as i've talked about in a previous video
the async and weight syntax is the
mechanism to achieve this
if you write async in front of a method
or function you indicate that it's
allowed to run this method or function
concurrently
a weight gives you control over the
order that things are being executed in
if you write a weight in front of a
concurrent statement this means that the
portion written below that statement can
only be executed after the concurrent
statement has completed being able to do
this is important when the next part of
your code relies on the result of the
previous part and this is often the case
you need to wait until you get the data
back from the database or you need the
confirmation from the api that your user
is logged in in order to continue and so
on i want to start with a quick recap of
how concurrent programming in python
works so for this you need to use the
async and weight syntax i have a simple
example program here that retrieves
pokemon names so i'm using a free api
here to do this so as you can see here
is a synchronous version of that code
it's a function get a random pokemon
name that picks an id between one and
the maximum pokemon id that's available
we have the url and we construct it
using this pokemon id and then i'm using
a function http getsync this function i
made myself i'm going to explain later
on how this works exactly and then we
return the name of the pokemon as a
string here we have an asynchronous
version which does exactly the same
thing but uses http get which is another
function that gets data yeah using a get
request but this one works
asynchronously this one works
concurrently and it also returns a name
currently i'm using the non-concurrent
version of this code this just gets a
pokemon name synchronously and then
prints that name and when you run this
then this is what you get so we now get
a nice pokemon name randomly selected
changing the code to use the concurrent
version is really easy in this case what
we need to do is we need to change main
into an asynchronous function as well
like so and now that we've made the main
function asynchronous we can add an
await
and then
of course i'm also going to have to
remove the
underscore sync here
like so and now main is asynchronous the
only thing we still need to do is to
make sure that main is run
asynchronously and that's by using the
asyncho dot run function
there we go and now if we run the code
again we should get exactly the same
result except of course now we get a mew
2 which is a different pokemon now for
the moment there's not a big advantage
in using concurrent programming here
because we're just doing a single http
request
but suppose you want to do multiple
requests instead of only one so the way
you could do that is for example by
running a for loop
so now we're running a for loop oh i
should remove this column here and put
it there so now we have for loop that
retrieves a pokemon name 20 times and
prints out the name and then this is
what happens
it takes quite a while because every
time we're launching a new request we're
waiting for that request to complete and
then we call the next request so this
takes a few seconds now this is where
you can really benefit from concurrent
programming because
why do we have to wait for every request
to complete before we can send out the
next request obviously it's possible
that the api that you're using has a
rate limiting factor so you can't send
thousands of requests within a single
second but you can definitely batch
things so you don't have to wait every
time and this is where we can rely on
the possibilities of async io so let me
write an alternative to this for loop
using asyncio.gather
so what i'm now going to do is provide
gather with a sequence of get pokemon
name calls
and i'm going to use a list
comprehensive for this and then unpack
it
and let me use the same kind of for loop
inside that list comprehension so then
this is what we get and now we write
a weight in front of it and then let's
have a
result so now the result is going to be
a tuple of strings and then we can print
this as well
like so so let me put this
into comments
like so and now let's run this program
again
and as you can see it's now much faster
let's take a look at the time to see how
much faster this actually is so
from
time i'm going to import
the performance counter function and
then let's store the time
before
and then let's print the
total time
in the synchronous case
and that's going to be
performance counter
minus the time before
there we go
and let's also do the same thing for the
gather option
so i'm going to copy this line
paste it here
and i'm also going to copy this line and
paste it here
and this is going to be the asynchronous
version so now if we run this code again
then this is what we get
so the synchronous version took over two
seconds and the asynchronous version
took
0.2 seconds so that's a 10 times
increase in efficiency and all of that
happens because we're just sending out
the requests all at once instead of
waiting for every request to complete
async and await are actually pretty well
integrated into python especially since
3.10 let's take a look at a few things
you can do with them one thing you can
do is combine the async and await syntax
with generators so for example let me
write a function that's called next
pokemon
which is going to give me
the next pokemon from a range of totals
so next pokemon this is
getting one argument which is a total
and then this is going to give me an
async iterable object and let's say that
returns a string we just want the
pokemon name so what this does is
we put the for loop in this
that's going over the total
range
range
there we go
and the name is
await
get random
pokemon name
and then we're going to yield
the name so this is a generator now in
the main loop it's really easy to
retrieve the next pokemon names like so
and let's say we want the next 20
pokemon
and we're going to print the name
and let's just
leave it like this and now let's run the
code and see what happens so as you can
see asynchronous generators work but it
still calls these things in order if you
want a different behavior you need to
use gather like i showed you just before
another thing you can do is create
asynchronous list comprehensions and
that works in exactly the same way so
instead of doing this for loop here what
you then do is names equals and then
we're going to create a list
comprehension name
async
for
name in
next
pokemon
20 and then let's also print
the names
like so and now when we run this code as
you can see we get more or less the same
timing so it's still doing these in
sequence but now we have them in a list
and again as i said if you want
different behavior use gather that's
going to run them concurrently another
thing i'd like to show you now is how to
turn
non-asynchronous code blocking code into
code that you can run concurrently by
the way if you're enjoying this video so
far give the like let me know if this is
helpful to you in the comments so
i have here another example which has a
couple of functions in there there is an
asynchronous counter function that
starts a counter and then goes to a
range there is a sleep function call so
this simply tells the
interpreter to to wait in other words
you can run other tasks concurrently and
then it prints out what's asleep for a
number of milliseconds there's also a
send request call that sends some http
request to a url and returns the status
code and my main function which is an
asynchronous function then sends that
request to rmcodes.com which is my
website and we get an http response with
the status code and then it awaits the
counter so if i run this then this is
what happens we get the hp request
status 200 after we get the response we
start the counter now what should you do
in order to make these things run
concurrently problem is that send
request is not concurrent code at the
moment it's blocking so one way you
might think you could fix this is by
using the async io create task function
so we already create the task
that's create task
and we're going to provide it the call
to the counter function and then
instead of awaiting the counter here
we're just going to await the task here
and then maybe it already starts the
task and does this at the same time
right so let's try this and see what
happens
so as you can see it didn't change
anything we still first have to wait
until we get the http response and then
we start the counter so that was not the
solution another thing you might want to
try to do is to use async asyncio.gather
like we saw before but that also doesn't
work because send request is not
concurrent it's not asynchronous what we
actually need to do in order to solve
this is to turn send request into an
asynchronous function and there's a very
simple way to do this with async io and
for that we're going to use the to
thread function so let me create another
function here
async
send
async request
so this is going to be a wrapper around
the synchronous send request function
so this is getting a url
and this returns a hint
in this function we're going to return
async io dot to thread which is the
function that we're going to use to turn
send request into an asynchronous
function
so going to provide to thread with the
send request function
and we're going to pass the url as an
argument to the to threat function as
well so it passes it along to the
synchronous function what this does
is that it creates a separate thread in
which to run that particular blocking
task in this case the call to the send
request function and then you can use it
as part of a concurrent program so then
here we're going to call the send async
request so we put an await in front of
that
and now we've turned this into an
asynchronous program one thing i forgot
is that we also need to write a weight
in front of this because we're awaiting
the two thread function so now when i
run this code again you see that we
start the counter we send the http
request and we're doing these things
concurrently so that works now and now
because we have two asynchronous
functions we can also use gather again
to do the same thing so instead of
writing this i could also write
something like this
so we're gathering the two function
calls and awaiting their result the
second one counter doesn't return
anything so i'm simply putting an
underscore here and then when i run this
we get exactly the same result when i
started explaining the pokemon example i
used an http get an http get function
that i said i coded up myself so what
does it look like well that's in this
module and these functions are actually
really simple so i'm using the request
package to do http requests so i have a
synchronous version here that calls the
get function of the request package and
then returns the response as a json
object and i have an asynchronous
version that uses async io.2 thread that
calls this function but then turns it
into something that you can run
concurrently so as you can see this is
really easy to use really easy to set up
you can use this kind of approach
whenever you need to interact with an
api and it's going to help you make your
code run much more efficiently because
you can group api calls which will save
you a lot of time waiting for the result
if you don't want to write a synchronous
code for doing these kinds of api
requests you can also use another
package for that that's called ao http
which i'm using here in this particular
example so here i have an alternative of
the http get function that uses ao http
creates a session and then the session
has a get function that you can call and
then it returns a response as a json
value just what you see here you also
see an example of using async with
context manager which is also possible
so in this case creating the session is
asynchronous and the get method is also
a context manager that you can run
concurrently using the with statement
here
i have a confession to make i don't
really like these nested with statements
so unless you really need to use the
session for some reason if you just want
to do simple get requests just use the
two thread function to do it in the way
that i just showed you it's much simpler
how does concurrent programming change
the way design patterns work in
principle not at all due to the very
clean await async syntax there is no
effect of coupling or cohesion by
introducing a synchronous code into your
application for example if you want to
use the strategy pattern you can create
an asynchronous method in your strategy
class and then call that and await it
if you want to use the factory
asynchronously no problem create objects
asynchronously if you want to the basic
pattern doesn't change just parts of the
pattern become available on the
architectural level concurrent
programming might have some impact
depending on what you do
you can imagine that if you're
processing data in a pipeline that
relies on asynchronous operations which
is quite common you'd want to adapt the
data structure so that it allows you to
specify what to run concurrently versus
what to run in sequence i gave an
example of this in a previous video
where i showed you how you can create a
nested structure of sequential and
concurrent function calls if you'd like
me to go into more detail on this in
another video let me know in the
comments below as you can see there are
lots of ways you can use concurrent code
in python to write more efficient
programs i hope you enjoyed this video
if you did give the like consider
subscribing to my channel if you want to
learn more about software design and
development thanks for watching take
care and see you soon
