Back to Python
2026-04-146 min read

Async Promises (Python Programming)

Learn Async Promises (Python Programming) step by step with clear examples and exercises.

Why This Matters

Asynchronous programming is crucial in handling I/O-bound tasks efficiently in Python, particularly for web applications and data-intensive operations. By allowing multiple tasks to run concurrently without blocking each other, asynchronous programming can significantly improve performance and scalability. Async Promises offer a cleaner and more manageable way to handle asynchronous operations compared to traditional callbacks or threading. They provide several advantages such as better error handling, easier code readability, and improved maintainability.

Prerequisites

Before diving into Async Promises, you should have a basic understanding of Python syntax, functions, and classes. Familiarity with synchronous programming concepts is essential, along with an understanding of the concept of event loops and coroutines. It's recommended to have some experience working with Python's concurrent.futures module as well.

Essential Concepts:

  • Basic Python syntax (variables, functions, classes)
  • Synchronous programming concepts (sequential execution, blocking I/O operations)
  • Event loops and coroutines
  • Python's concurrent.futures module

Core Concept

Async Promises are a part of Python's concurrent.futures module and allow developers to write asynchronous code using a promise-based approach, similar to JavaScript's Promise object. An AsyncPromise is an object that represents the eventual completion or failure of an asynchronous operation and its resulting value.

Async Promises are implemented using coroutines and the async/await syntax introduced in Python 3.5. A coroutine is a special type of function that can be paused and resumed, allowing other coroutines to run in between. The async keyword marks a function as a coroutine, while await pauses the execution of the coroutine until the promise is resolved or rejected.

Key Components:

  • Coroutines (functions that can be paused and resumed)
  • async keyword (marks a function as a coroutine)
  • await keyword (pauses the execution of a coroutine)

Worked Example

In this section, we'll walk through a more complex example that demonstrates how to use Async Promises to download multiple files concurrently and process them using a callback function.

import asyncio
import os
import urllib.request

async def download_file(url, filename):
response = await asyncio.get_event_loop().run_in_executor(None, urllib.request.urlopen, url)
with open(filename, 'wb') as f:
while True:
data = await response.read(8192)
if not data:
break
f.write(data)

async def process_file(filename):
print(f'Processing file {filename}...')

Process the downloaded file here (e.g., extracting data, validating content, etc.)

async def main():

urls = [

'https://example.com/file1.txt',

'https://example.com/file2.txt',

'https://example.com/file3.txt'

]

tasks = []

for url in urls:

filename = os.path.basename(url)

download_task = download_file(url, filename)

process_task = process_file(filename)

await asyncio.gather(download_task, process_task)

tasks.append((download_task, process_task))

async def cleanup():

for (_, process_task) in tasks:

await process_task

if __name__ == "__main__":

asyncio.run(asyncio.gather(main(), cleanup()))


In this example, we define an `download_file` AsyncPromise that downloads a file from a given URL and saves it to a local file with the same name as the URL's filename. We also create a separate `process_file` function that processes each downloaded file (e.g., extracting data, validating content, etc.). The `main` function creates a list of URLs, creates an AsyncPromise for each URL, and pairs it with its corresponding process task.

The `asyncio.gather` function is used to run both the download and process tasks concurrently. After all tasks are completed, we clean up by waiting for any remaining process tasks using the `cleanup` coroutine.

Common Mistakes

  1. ### Forgetting the await keyword:

When using Async Promises, you must use the await keyword before any coroutine that returns a Promise object. Failing to do so will result in a runtime error.

  1. ### Mixing synchronous and asynchronous code:

Carefully manage when and where you use synchronous and asynchronous code. Mixing them improperly can lead to unexpected behavior and performance issues.

  1. ### Not handling exceptions properly:

When working with Async Promises, it's essential to handle exceptions appropriately. If an exception occurs within an AsyncPromise, it will be propagated up the call stack and must be handled at the appropriate level.

  1. ### Ignoring the event loop:

Async Promises rely on the event loop to run tasks concurrently. Make sure you're using the correct event loop (e.g., the global event loop) when creating and running AsyncPromises.

  1. ### Not canceling long-running tasks:

If a task takes too long or encounters an error, it can block other tasks from running. To avoid this, consider implementing a mechanism to cancel long-running tasks or handle errors gracefully.

  1. ### Using the wrong context manager for I/O operations:

When using async context managers like async with open(...) as f, ensure that the underlying I/O operation supports asynchronous operations. For example, use aiofiles instead of the standard library's open function for asynchronous file operations.

  1. ### Not understanding yield from:

The yield from statement is used to execute another coroutine and return its results. It can help simplify complex asynchronous code by allowing you to treat a sequence of coroutines as if they were a single coroutine.

Practice Questions

  1. Write an AsyncPromise that fetches data from multiple URLs concurrently and returns a list of the results using a callback function for processing each result.
  2. Implement an AsyncPromise for reading lines from a large text file asynchronously, one line at a time, and storing them in a list.
  3. Create an AsyncPromise that downloads multiple images from the internet and saves them to disk, using a callback function to validate each image's content after it has been downloaded.
  4. Write an AsyncPromise for fetching data from a REST API using HTTP requests and processing the response using a callback function. The AsyncPromise should return the processed data when all requests have completed.
  5. Implement an AsyncPromise that performs a long-running calculation asynchronously, allowing other tasks to run while the calculation is in progress. Include a mechanism for canceling the calculation if necessary.
  6. Write an AsyncPromise that downloads multiple files concurrently and validates each file's content using a callback function. If any file fails validation, the AsyncPromise should reject with an error message containing the failed file's name.
  7. Implement an AsyncPromise for reading data from multiple databases asynchronously, one record at a time, and returning the combined results using a callback function for processing each record.
  8. Create an AsyncPromise that sends multiple emails concurrently using SMTP and processes the responses using a callback function to handle delivery failures or errors. The AsyncPromise should return the number of successfully delivered emails when all tasks have completed.
  9. Write an AsyncPromise that performs a series of asynchronous tests (e.g., unit tests, integration tests) and returns the test results when all tests have completed. If any test fails, the AsyncPromise should reject with an error message containing the failed test's name and description.
  10. Implement an AsyncPromise that fetches data from multiple APIs using HTTP requests, where each API may have different response times. The AsyncPromise should return the combined results when all requests have completed, sorted by the order in which they were sent.

FAQ

What is the difference between a coroutine and an AsyncPromise?

A coroutine is a special type of function that can be paused and resumed, allowing other coroutines to run in between. An AsyncPromise represents the eventual completion or failure of an asynchronous operation and its resulting value.

Can I mix synchronous and asynchronous code in the same program?

While it's possible to mix synchronous and asynchronous code, it can lead to unexpected behavior and performance issues. It's recommended to carefully manage when and where you use each type of code to ensure proper execution.

How do I handle exceptions in Async Promises?

When working with Async Promises, it's essential to handle exceptions appropriately. If an exception occurs within an AsyncPromise, it will be propagated up the call stack and must be handled at the appropriate level.

What is the role of the event loop in Async Promises?

Async Promises rely on the event loop to run tasks concurrently. Make sure you're using the correct event loop (e.g., the global event loop) when creating and running AsyncPromises.

How can I cancel long-running tasks in Async Promises?

To avoid blocking other tasks, consider implementing a mechanism to cancel long-running tasks or handle errors gracefully. This can help improve performance and prevent unexpected behavior.

Async Promises (Python Programming) | Python | XQA Learn