Asynchronicity - my new town

Python coding? Always been my jam. But there's this thing that used to bug me – all those 'asyncio', 'async/await', and 'concurrency' terms that kept popping up. I felt like they were gatecrashing my Python party. Sure, I played along, wrote some asynchronous functions, and slapped await on them straight away. I was missing the point, like trying to play a guitar solo on a new electric guitar while it's still in the case. That guitar solo? Python's asynchronous capabilities.

At first, 'asynchronous programming' just sounded like 'complicated hassle'. It felt like I'd been handed a new games console but without the user manual. But here's the game-changer – it's not about doing more, it's about doing things differently. It’s like ordering your coffee while you're still in line at the bakery. It let me run tasks concurrently, not sequentially. It was a lightbulb moment, like realizing that the fast-forward button also skips ads. It gave me a whole new way to speed up my programs, particularly those tangled up with IO-bound tasks like HTTP requests, database calls, and reading from or writing to files.

It felt less like an upgrade and more like discovering a cheat code. Async programming isn't some kind of show-off skill; it's more of a superpower once you get it.

I went from thinking 'async' was a four-letter word, to realizing it's a secret handshake in the world of Python programming. And that's a turn of events I can get behind.

Mastering My Tools: The async/await Keywords and Coroutines

To master this new ability, I had to understand my tools. First async and await. An async def function is a coroutine—a special kind of function that can pause and resume at my command. When I summon an async function, it yields a coroutine object, but it doesn't start running yet. To unleash it, I await the coroutine.

In my early attempts, I would call async functions and instantly await them:

async def fetch_data():
    # Imagine we're doing something IO-bound here...
    pass

async def main():
    await fetch_data()

# Run the main function
asyncio.run(main())

The function fetch_data sprang into action as soon as it was awaited and main stood still until fetch_data completed. To me, this felt no different than a mere mortal performing tasks in a synchronous manner. Which is actually was!

My Moment of Eureka: Supercharging Concurrency with asyncio

The moment I discovered the power of asyncio.create_task(), it was as though I had unlocked an advanced feature. This feature allowed me to schedule a coroutine to run concurrently. When I created a task, Python sprung the coroutine into action, but instead of freezing the rest of my program, it gave me the freedom to perform other tasks.

Here's a demonstration:

async def main():
    # Create a task: start running fetch_data but don't wait for it yet
    task = asyncio.create_task(fetch_data())

    # Do some other work here...

    # Now we're ready to use the data fetched by fetch_data, so we await the task
    result = await task

In this snippet, I don't await fetch_data() instantly, letting main() continue its operations concurrently with fetch_data(). Multiple tasks at once! The try async way.

Supercharging My Network Requests: asyncio in Action

When it came to fetching data from multiple URLs, oh boy that was the time to shine. Initially, I made requests sequentially, a mere mortal awaiting each one to complete before initiating the next. With my newfound abilities, I realized I could dispatch all the requests at once and then gather their results later, gaining a massive boost in performance.

Here's a snapshot of how I created multiple tasks concurrently:

async def main():
    urls = [/* a list of URLs */]

    # Start all fetch operations
    tasks = [asyncio.create_task(fetch_comments(url)) for url in urls]

    # Now collect the results
    comments = [await task for task in tasks]

    print(comments)

asyncio.run(main())

Wrapping Up

Acquiring this new superpower has been an exhilarating journey. From the first sparks of understanding asynchronous programming, mastering the incantations of async/await keywords, to supercharge my programs with the might of asyncio.create_task(), I have unlocked a powerful capability.

I am at the beginning of an exciting path, filled with opportunities to learn and grow. There are still corners of this topic that I need to explore. Every new task is an opportunity to experiment with this new ability.

Now, when I code, I do with more knowledge about asynchronous. My Python programs are no longer just sequences of commands; they are symphonies of concurrent tasks performing a ballet of efficiency. At least that would be my fantasy.

And so, fellow coders, this is just a start for a new chapter. Continue to learn, continue to explore, and continue to grow. Who knows what superpowers you might discover next in your own journey?

Subscribe to Eduard Schwarzkopf

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe