Python asyncio: Adding schedule callbacks to a Future

Updated: February 11, 2024 By: Guest Contributor Post a comment

Introduction

In this tutorial, we’ll explore how to use Python’s asyncio library to add scheduled callbacks to a Future object. Asyncio has become an essential part of Python for asynchronous programming, allowing for the execution of multiple tasks seemingly at the same time. Here, we’ll dive deep into the Future object, a key component of asyncio for handling eventual results from asynchronous operations, and see how to attach callbacks to it, enhancing our asynchronous programming techniques.

The Basics

Before we delve into how to add scheduled callbacks, let’s first understand what a Future object is. In asyncio, a Future represents the result of an operation that hasn’t completed yet. It acts as a placeholder, allowing you to query the result at a future point in time.

Basic Example: Creating a Future

import asyncio

async def set_after(fut, delay, value):
    # Wait for a specific time
    await asyncio.sleep(delay)
    # Set the value of the future
    fut.set_result(value)

async def main():
    # Create a Future object
    fut = asyncio.Future()
    # Schedule to set the future's result after 2 seconds
    await set_after(fut, 2, 'Hello, World!')
    # Once set, retrieve and print the future's result
    print(await fut)

asyncio.run(main())

Output: Hello, World!

This simple example demonstrates how to create a Future object and assign its result after a delay. It showcases the basic concept of futures in asyncio and how they can store the result of any asynchronous operation for later retrieval.

Adding Callbacks to Futures

Once you’ve got a handle on creating Future objects, the next step is to understand how to add callbacks. A callback is a function that is called once the future is done, allowing for additional operations or processing to be executed based on the future’s outcome.

def my_callback(future):
    print('This is my callback:', future.result())

async def main():
    fut = asyncio.Future()
    # Add a callback to the future
    fut.add_done_callback(my_callback)
    # Set the future's result to trigger the callback
    await set_after(fut, 1, 'Callback Triggered')

asyncio.run(main())

This code demonstrates how to add a simple callback to a future. Once the future’s result is set, our callback is triggered, displaying a message along with the result of the future.

Advanced Example: Using asyncio with Multiple Callbacks

import asyncio

async def set_value(fut, value):
    fut.set_result(value)

def callback1(future):
    print('Callback 1:', future.result())

def callback2(future):
    print('Callback 2:', future.result())

async def main():
    fut = asyncio.Future()
    # Add multiple callbacks
    fut.add_done_callback(callback1)
    fut.add_done_callback(callback2)
    # Set the future's result
    await set_value(fut, 'Multiple Callbacks!')

asyncio.run(main())

Here, we’ve taken the example a step further by adding multiple callbacks to a single future. This approach demonstrates the flexibility of asyncio’s Future objects, allowing you to sequentially execute different functions once the future completes. This is incredibly useful for complex workflows where different steps may be required after the completion of an asynchronous task.

Scheduled Callbacks: The Next Level

Going beyond simple and multiple callbacks, we can also schedule callbacks to run at specific times or after certain conditions are met, adding another layer of sophistication to our asynchronous programming.

import asyncio

def schedule_callback(fut, callback, delay):
    async def delayed_callback():
        await asyncio.sleep(delay)
        callback(fut)
    asyncio.create_task(delayed_callback())

async def main():
    fut = asyncio.Future()
    # Schedule a callback to be called after 2 seconds
    schedule_callback(fut, lambda f: print('Delayed callback:', f.result()), 2)
    # This time, set the future's result immediately
    fut.set_result('Immediate Result')

asyncio.run(main())

This example showcases how to implement scheduled callbacks using asyncio. By employing a delay, you can ensure that callbacks are executed not immediately when the future’s result is set, but after a specified period, enabling more controlled flows and timing of operations.

Conclusion

In this tutorial, we have explored how to use Python’s asyncio library to add scheduled callbacks to a Future object. Through practical examples, we demonstrated the basic and advanced usages of Futures and callbacks in asynchronous programming, highlighting their importance and versatility in managing asynchronous operations efficiently.