asynscio

Introduction to asyncio in Python for Beginners

December 17, 20244 min read

Beginner's Guide to asyncio in Python

asyncio is a Python library that helps you run multiple tasks at the same time without waiting for one to finish before starting another. This is very useful when your program needs to wait for things like file reading, network calls, or database access.

If you are new to asyncio, don't worry! This guide will explain it step by step in simple terms.


1. What is Asynchronous Programming?

In normal programming (called synchronous programming), tasks happen one after another. If Task A takes 5 seconds, Task B will only start after Task A finishes.

In asynchronous programming, you can start Task A, do other work while waiting for it to finish, and then come back to Task A when it's ready. This saves time and makes your program faster when dealing with tasks like I/O (input/output).

Example:

  • Synchronous: Finish Task A -> Wait -> Start Task B.

  • Asynchronous: Start Task A -> Do other work -> Return to Task A -> Start Task B.


2. Basic Terms in asyncio

a) Event Loop

The event loop is the "brain" of asyncio. It decides which task runs and when. It keeps checking for tasks to run and manages them efficiently.

b) Coroutines

Coroutines are special functions in Python that support asynchronous programming. They are created using async def.

import asyncio

async def my_task():
    print("Hello, Asyncio!")

c) await Keyword

The await keyword is used to pause a coroutine until the task is finished. It tells the program to "wait here" and come back when ready.

async def do_something():
    await asyncio.sleep(2)  # Wait for 2 seconds
    print("Task done!")

d) Tasks

Tasks are used to run coroutines. asyncio.create_task() schedules a coroutine to run in the background.


3. Simple asyncio Program

Here is a basic example to get started:

import asyncio

async def say_hello():
    print("Hello!")
    await asyncio.sleep(1)  # Pause for 1 second
    print("Hello again!")

async def main():
    print("Program started")
    await say_hello()
    print("Program finished")

asyncio.run(main())

Output:

Program started
Hello!
Hello again!
Program finished

Explanation:

  • async def defines the coroutine.

  • await tells the program to pause and wait.

  • asyncio.run(main()) starts the program.


4. Running Multiple Coroutines with asyncio.gather()

Here is an example of running two coroutines at the same time using asyncio.gather():

import asyncio

async def say_hello1():
    print("Hello1!")
    await asyncio.sleep(1)  # Pause for 1 second
    print("Hello1 again!")

async def say_hello2():
    print("Hello2!")
    await asyncio.sleep(1)  # Pause for 1 second
    print("Hello2 again!")

async def main():
    print("Program started")
    await asyncio.gather(say_hello1(), say_hello2())
    print("Program finished")

asyncio.run(main())

Output:

Program started
Hello1!
Hello2!
Hello1 again!
Hello2 again!
Program finished

Explanation:

  • asyncio.gather() runs say_hello1() and say_hello2() at the same time.

  • Both coroutines start, and the program pauses for 1 second for each.

  • After both finish, the program continues.


5. Using asyncio.run in Real-World Examples

The asyncio.run() function is used to start the event loop and run your main coroutine. Below is a practical example where we fetch historical and real-time data using the Alpaca API for cryptocurrencies like BTC/USD and ETH/USD.

import asyncio
from alpaca.data.historical.crypto import CryptoHistoricalDataClient
from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
from alpaca.data.timeframe import TimeFrame, TimeFrameUnit
from alpaca.data.requests import CryptoBarsRequest
from alpaca.data.live.crypto import CryptoDataStream
from credentials import api_key, secret_key

# Initialize Historical Data Client
crypto_historical_data_client = CryptoHistoricalDataClient()

# Historical Data Retrieval Function
async def get_history():
    while True:
        symbol = "BTC/USD"
        now = datetime.now(ZoneInfo("America/New_York"))
        req = CryptoBarsRequest(
            symbol_or_symbols=[symbol],
            timeframe=TimeFrame(amount=1, unit=TimeFrameUnit.Minute),
            start=now - timedelta(days=1),
        )
        history_df2 = crypto_historical_data_client.get_crypto_bars(req).df
        print("Historical Data:")
        print(history_df2)
        await asyncio.sleep(10)  # Wait for 10 seconds before fetching again

# Initialize Data Stream Client
crypto_data_stream_client = CryptoDataStream(api_key, secret_key)

# Live Data Stream Handler
async def crypto_data_stream_handler(data):
    print(data)

# Function to Start the Live Stream
async def start_crypto_stream():
    symbol = ['BTC/USD', 'ETH/USD']
    crypto_data_stream_client.subscribe_quotes(crypto_data_stream_handler, *symbol)
    print("Starting Live Data Stream...")
    
    await crypto_data_stream_client._run_forever()  # Await the internal run method

# Main Function to Run Both Tasks Concurrently
async def main():
    # Run both tasks concurrently
    await asyncio.gather(
        get_history(),            # Historical data retrieval every 10 seconds
        start_crypto_stream()     # Live data stream
    )

asyncio.run(main())

What Happens Here:

  1. Historical Data:

    • The get_history function fetches historical data for the last day for BTC/USD every 10 seconds.

  2. Live Stream:

    • The start_crypto_stream function subscribes to real-time data for BTC/USD and ETH/USD using the Alpaca live stream API.

  3. Concurrent Execution:

    • Using asyncio.gather, both tasks (historical and real-time data fetching) run at the same time.


6. Common Use Cases for asyncio

  • Web Scraping: Downloading many web pages at the same time.

  • APIs: Sending and receiving data without blocking the program.

  • Servers: Handling multiple users at once.

  • Databases: Querying databases efficiently.

  • Delays: Simulating waiting times.


7. Tips for Beginners

  1. Always use asyncio.run() to start your program.

  2. Use await only for I/O tasks like network requests or waiting.

  3. Avoid blocking calls like time.sleep(); use asyncio.sleep() instead.

  4. Use asyncio.gather() to run tasks concurrently.


8. Summary

asyncio helps you write programs that do multiple tasks at the same time, especially when waiting for input/output operations. It is easy to start with async def, await, and asyncio.run().

With a bit of practice, you can use asyncio to build faster and more efficient programs. Try the examples here and experiment with your own tasks.

asynscio
blog author image

sunil s

Quant Developer & Mentor

Back to Blog