In Python, the async for
construct allows you to iterate over asynchronous iterators, which yield values from asynchronous operations. You’ll use it when working with asynchronous libraries or frameworks where data fetching or processing happens asynchronously, such as reading from databases or making HTTP requests. The async for
loop ensures that while waiting for data, other tasks can run concurrently, improving efficiency in I/O-bound tasks.
Here’s a minimal example:

import asyncio async def async_gen(): for i in range(3): await asyncio.sleep(1) # Simulate asynchronous I/O operation yield i async def main(): async for val in async_gen(): print(val) # To run the code: asyncio.run(main())
In this example, async_gen
is an asynchronous generator that yields numbers from 0 to 2. Each number is yielded after waiting for 1 second (simulating an asynchronous operation). The main
function demonstrates how to use the async for
loop to iterate over the asynchronous generator.
Understanding Python Async Keyword
As a Python developer, you might have heard of asynchronous programming and how it can help improve the efficiency of your code.
One powerful tool for working with asynchronous code is the async for
loop, which allows you to iterate through asynchronous iterators while maintaining a non-blocking execution flow. By harnessing the power of async for
, you will be able to write high-performing applications that can handle multiple tasks concurrently without being slowed down by blocking operations.
The async for
loop is based on the concept of asynchronous iterators, providing a mechanism to traverse through a series of awaitables while retrieving their results without blocking the rest of your program. This distinct feature sets it apart from traditional synchronous loops, and it plays an essential role in making your code concurrent and responsive, handling tasks such as network requests and other I/O-bound operations more efficiently.
To get started with async for
in Python, you’ll need to use the async def
keyword when creating asynchronous functions, and make use of asynchronous context managers and generators.
When you deal with asynchronous programming in Python, the async
keyword plays a crucial role. Asynchronous programming allows your code to handle multiple tasks simultaneously without blocking other tasks. This is particularly useful in scenarios where tasks need to be executed concurrently without waiting for each other to finish.
The async
keyword in Python signifies that a function is a coroutine. Coroutines are a way of writing asynchronous code that looks similar to synchronous code, making it easier to understand. With coroutines, you can suspend and resume the execution of a function at specific points, allowing other tasks to run concurrently.
In Python, the async
keyword is used in conjunction with the await
keyword. While async
defines a coroutine function, await
is used to call a coroutine and wait for it to complete. When you use the await
keyword, the execution of the current coroutine is suspended, and other tasks are allowed to run. Once the await
expression completes, the coroutine resumes its execution from where it left off.
π‘ Recommended: Python Async Await: Mastering Concurrent Programming
Here’s an example of how you might use the async
and await
keywords in your Python code:

import aiohttp import asyncio async def fetch_url(url): async with aiohttp.ClientSession() as session: async with session.get(url) as response: return await response.text() async def main(): url = "https://www.example.com/" content = await fetch_url(url) print(content) asyncio.run(main())
In this example, fetch_url
is a coroutine defined using the async
keyword. It makes a request to a specified URL and retrieves the content. The request and response handling is done asynchronously, allowing other tasks to run while waiting for the response. The main
coroutine uses await
to call fetch_url
and waits for it to complete before printing the content.
Async Function and Coroutine Objects
In Python, asynchronous programming relies on coroutine objects to execute code concurrently without blocking the execution flow of your program. You can create coroutine objects by defining asynchronous functions using the async def
keyword. Within these async functions, you can use the await
keyword to call other asynchronous functions, referred to as async/await syntax.
To begin, define your asynchronous function using the async
keyword, followed by def
:
async def my_async_function(): # your code here
While working with asynchronous functions, you’ll often encounter situations where you need to call other async functions. To do this, use the await
keyword before the function call. This allows your program to wait for the result of the awaited function before moving on to the next line of code:
async def another_async_function(): # your code here async def my_async_function(): result = await another_async_function()
Coroutine objects are created when you call an async function, but the function doesn’t execute immediately. Instead, these coroutines can be scheduled to run concurrently using an event loop provided by the asyncio
library. Here’s an example of running a coroutine using asyncio.run()
:
import asyncio async def my_async_function(): print("Hello, async!") asyncio.run(my_async_function())
Remember that async functions are not meant to be called directly like regular functions. Instead, they should be awaited within another async function or scheduled using an event loop.
By using coroutine objects and the async/await syntax, you can write more efficient, readable, and performant code that manages concurrency and handles I/O bound tasks effectively. Keep in mind that async functions should primarily be used for I/O-bound tasks and not for CPU-bound tasks. For CPU-bound tasks, consider using multi-threading or multi-processing instead.
The Fundamentals of AsyncIO
π‘ AsyncIO is a Python library that provides support for writing asynchronous code utilizing the async
and await
syntax. It allows you to write concurrent code in a single-threaded environment, which can be more efficient and easier to work with than using multiple threads.
To start using AsyncIO, you need to import asyncio in your Python script. Once imported, the core component of AsyncIO is the event loop. The event loop manages and schedules the execution of coroutines, which are special functions designed to work with asynchronous code. They are defined using the async def
syntax.
Creating a coroutine is simple. For instance, hereβs a basic example:
import asyncio async def my_coroutine(): print("Hello AsyncIO!") asyncio.run(my_coroutine())
In this example, my_coroutine
is a coroutine that just prints a message. The asyncio.run()
function is used to start and run the event loop, which in turn executes the coroutine.
π‘ Coroutines play a crucial role in writing asynchronous code with AsyncIO. Instead of using callbacks or threads, coroutines use the await
keyword to temporarily suspend their execution, allowing other tasks to run concurrently. This cooperative multitasking approach lets you write efficient, non-blocking code.
Here is an example showcasing the use of await
:
import asyncio async def say_after(delay, message): await asyncio.sleep(delay) print(message) async def main(): await say_after(1, "Hello") await say_after(2, "AsyncIO!") asyncio.run(main())
In this example, the say_after
coroutine takes two parameters: delay
and message
. The await asyncio.sleep(delay)
line is used to pause the execution of the coroutine for the specified number of seconds. After the pause, the message
is printed. The main
coroutine is responsible for running two instances of say_after
, and the whole script is run via asyncio.run(main())
.
Asynchronous For Loop
In Python, you can use the async for
statement to iterate asynchronously over items in a collection. It allows you to perform non-blocking iteration, making your code more efficient when handling tasks such as fetching data from APIs or handling user inputs in a graphical user interface.
In order to create an asynchronous iterator, you need to define an object with an __aiter__()
method that returns itself, and an __anext__()
method which is responsible for providing the next item in the collection.
For example:
class AsyncRange: def __init__(self, start, end): self.start = start self.end = end def __aiter__(self): return self async def __anext__(self): if self.start >= self.end: raise StopAsyncIteration current = self.start self.start += 1 return current
Once you have your asynchronous iterator, you can use the async for
loop to iterate over the items in a non-blocking manner. Here is an example showcasing the usage of the AsyncRange
iterator:
import asyncio async def main(): async for number in AsyncRange(0, 5): print(number) await asyncio.sleep(1) asyncio.run(main())
In this example, the AsyncRange
iterator is used in an async for
loop, where each iteration in the loop pauses for one second using the await asyncio.sleep(1)
line. Despite the delay, the loop doesn’t block the execution of other tasks because it is asynchronous.
It’s important to remember that the async for
, __aiter__()
, and __anext__()
constructs should be used only in asynchronous contexts, such as in coroutines or with async context managers.
By utilizing the asynchronous for loop, you can write more efficient Python code that takes full advantage of the asynchronous programming paradigm. This comes in handy when dealing with multiple tasks that need to be executed concurrently and in non-blocking ways.
Using Async with Statement
When working with asynchronous programming in Python, you might come across the async with
statement. This statement is specifically designed for creating and utilizing asynchronous context managers. Asynchronous context managers are able to suspend execution in their __enter__
and __exit__
methods, providing an effective way to manage resources in a concurrent environment.
To use the async with
statement, first, you need to define an asynchronous context manager. This can be done by implementing an __aenter__
and an __aexit__
method in your class, which are the asynchronous counterparts of the synchronous __enter__
and __exit__
methods used in regular context managers.
The __aenter__
method is responsible for entering the asynchronous context, while the __aexit__
method takes care of exiting the context and performing cleanup operations.
Here’s a simple example to illustrate the usage of the async with
statement:
import aiohttp import asyncio async def fetch_data(url): async with aiohttp.ClientSession() as session: async with session.get(url) as response: return await response.text() async def main(): url = "https://example.com" data = await fetch_data(url) print(data) asyncio.run(main())
In this example, we’re using the aiohttp
library to fetch the contents of a webpage. By using async with
when creating the ClientSession
and the session.get
contexts, we ensure that resources are effectively managed throughout their lifetime in an asynchronous environment.
Time and Delays in Async
The time
and asyncio.sleep
functions are essential components of managing time delays.
In asynchronous programming, using time.sleep
is not recommended since it can block the entire execution of your script, causing it to become unresponsive. Instead, you should use asyncio.sleep
, which is a non-blocking alternative specifically designed for asynchronous tasks.
To implement a time delay in your async function, simply use the await asyncio.sleep(seconds)
syntax, replacing seconds
with the desired number of seconds for the delay. For example:

import asyncio async def delay_task(): print("Task started") await asyncio.sleep(2) print("Task completed after 2 seconds") asyncio.run(delay_task())
This will cause a 2-second wait between printing “Task started” and “Task completed after 2 seconds” without blocking the overall execution of your script.
Timeouts can also play a significant role in async programming, preventing tasks from taking up too much time or becoming stuck in an infinite loop.
To set a timeout for an async task, you can use the asyncio.wait_for
function:

import asyncio async def long_running_task(): await asyncio.sleep(10) return "Task completed after 10 seconds" async def main(): try: result = await asyncio.wait_for(long_running_task(), timeout=5) print(result) except asyncio.TimeoutError: print("Task took too long to complete") asyncio.run(main())
In this example, the long_running_task
takes 10 seconds to complete, but we set a timeout of 5 seconds using asyncio.wait_for
. When the task exceeds the 5-second limit, an asyncio.TimeoutError
is raised, and the message “Task took too long to complete” is printed.
By understanding and utilizing asyncio.sleep
and timeouts in your asynchronous programming, you can create efficient and responsive applications in Python.
Concurrency with AsyncIO
AsyncIO is a powerful library in Python that enables you to write concurrent code. By using the async/await syntax, you can create and manage coroutines, which are lightweight functions that can run concurrently in a single thread or event loop. This approach maximizes efficiency and responsiveness in your applications, especially when dealing with I/O-bound operations.
To start, you’ll need to define your coroutines using the async def
keyword. This allows you to use the await
keyword within the coroutine to yield control back to the event loop, thus enabling other coroutines to run. You can think of coroutines as tasks that run concurrently within the same event loop.
To manage the execution of coroutines, you’ll use the asyncio.create_task()
function. This creates a task object linked to the coroutine which is scheduled and run concurrently with other tasks within the event loop. For example:
import asyncio async def my_coroutine(): print("Hello, World!") task = asyncio.create_task(my_coroutine())
To run multiple tasks concurrently, you can use the asyncio.gather()
function. This function takes several tasks as arguments and starts them all concurrently. When all tasks are completed, it returns a list of their results:
import asyncio async def task_one(): await asyncio.sleep(1) return "Task one completed" async def task_two(): await asyncio.sleep(2) return "Task two completed" async def main(): results = await asyncio.gather(task_one(), task_two()) print(results) asyncio.run(main())
Another useful function is asyncio.as_completed()
. This function returns an asynchronous iterator that yields coroutines in the order they complete. It can be helpful when you want to process the results of coroutines as soon as they are finished, without waiting for all of them to complete:

import asyncio async def my_task(duration): await asyncio.sleep(duration) return f"Task completed in {duration} seconds" async def main(): tasks = [my_task(1), my_task(3), my_task(2)] for coroutine in asyncio.as_completed(tasks): result = await coroutine print(result) asyncio.run(main())
When working with AsyncIO, remember that your coroutines should always be defined using the async
keyword, and any function that calls an asynchronous function should also be asynchronous.
Generators, Futures and Transports
In your journey with Python’s async programming, you will come across key concepts like generators, futures, and transports. Understanding these concepts will help you grasp the core principles of asynchronous programming in Python.
Generators are functions that use the yield
keyword to produce a sequence of values without computing them all at once. Instead of returning a single value or a list, a generator can be paused at any point in its execution, only to be resumed later. This is especially useful in async programming as it helps manage resources efficiently.
yield from
is a construct that allows you to delegate part of a generator’s operations to another generator, ultimately simplifying the code. When using yield from
, you include a subgenerator expression, which enables the parent generator to yield values from the subgenerator.
Futures represent the result of a computation that may not have completed yet. In the context of async programming, a future object essentially acts as a placeholder for the eventual outcome of an asynchronous operation. Their main purpose is to enable the interoperation of low-level callback-based code with high-level async/await code. As a best practice, avoid exposing future objects in user-facing APIs.
Transports are low-level constructs responsible for handling the actual I/O operations. They implement the communication protocol details, allowing you to focus on the high-level async/await code. Asyncio transports provide a streamlined way to manage sockets, buffers, and other low-level I/O related tasks.
Frequently Asked Questions
What are the main differences between ‘async for’ and regular ‘for’ loops?
The main difference between async for
and regular for
loops in Python is that async for
allows you to work with asynchronous iterators. This means that you can perform non-blocking I/O operations while iterating, helping to improve your program’s performance and efficiency. Regular for
loops are used with synchronous code, where each iteration must complete before the next one begins.
How can async for loop be implemented with list comprehensions?
Unfortunately, async for
cannot be directly used with list comprehensions since the syntax does not support asynchronous execution. Instead, when working with asynchronous code, you can use asyncio.gather()
alongside a list comprehension to achieve a similar result. This approach allows you to run multiple asynchronous tasks concurrently and collect their results.
For example:
import asyncio async def square(x): await asyncio.sleep(1) return x * x async def main(): numbers = [1, 2, 3, 4, 5] results = await asyncio.gather(*(square(num) for num in numbers)) print(results) asyncio.run(main())
What are common patterns to efficiently use async in Python?
To efficiently use async
in Python, you can employ the following patterns:
- Use
asyncio
library features, such asasyncio.gather()
,asyncio.sleep()
, and event loops. - Write asynchronous functions with the
async def
syntax and useawait
to call other asynchronous functions. - Use context managers, such as
async with
, to handle resources that support asynchronous operations. - Use
async for
loops when working with asynchronous iterators to keep your code non-blocking.
How can you create an async range in Python?
To create an async range in Python, you can implement an asynchronous iterator with a custom class that adheres to the async iterator protocol. The custom class should define an __aiter__()
method to return itself and implement an __anext__()
method that raises StopAsyncIteration
when the range is exhausted. Here is an example:
import asyncio class AsyncRange: def __init__(self, start, end): self.start = start self.end = end def __aiter__(self): return self async def __anext__(self): if self.start >= self.end: raise StopAsyncIteration current = self.start self.start += 1 await asyncio.sleep(1) return current
Are there any examples of creating an async iterator?
Here’s an example of creating an async iterator using a custom class:
import asyncio class AsyncCountdown: def __init__(self, count): self.count = count def __aiter__(self): return self async def __anext__(self): if self.count <= 0: raise StopAsyncIteration value = self.count self.count -= 1 await asyncio.sleep(1) return value async def main(): async for value in AsyncCountdown(5): print(value) asyncio.run(main())
What is the correct way to use ‘async while’ in Python?
To use async while
in Python, simply place the await
keyword before an asynchronous function or expression within the body of the while
loop. By doing so, the loop will execute the asynchronous code non-blocking, allowing other tasks to run concurrently. Here’s an example:
import asyncio async def async_while_example(): count = 5 while count > 0: await asyncio.sleep(1) print(count) count -= 1 asyncio.run(async_while_example())
π‘ Recommended: Python Async Function