Skip to content

gh-137026: Add an explainer guide for asyncio #137215

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 101 commits into
base: main
Choose a base branch
from

Conversation

anordin95
Copy link

@anordin95 anordin95 commented Jul 29, 2025

Explainer guide for asyncio

gh-137026: HOWTO article for asyncio, and a reference to it from the main page of the asyncio docs.


Hi!

I've used Python's asyncio a couple times now, but never really felt confident in my mental model of how it fundamentally works and therefore how I can best leverage it. The official docs provide good documentation for each specific function in the package, but, in my opinion, are missing a cohesive overview of the systems design and architecture. Something that could help the user understand the why and how behind the recommended patterns. And a way to help the user make informed decisions about which tool in the asyncio toolkit they ought to grab, or to recognize when asyncio is the entirely wrong toolkit. I thought I'd take a stab at filling that gap and contributing back to a community that's given so much!


📚 Documentation preview 📚: https://cpython-previews--137215.org.readthedocs.build/en/137215/howto/a-conceptual-overview-of-asyncio.html

@python-cla-bot

This comment was marked as resolved.

@bedevere-app bedevere-app bot added docs Documentation in the Doc dir skip news labels Jul 29, 2025
@github-project-automation github-project-automation bot moved this to Todo in Docs PRs Jul 29, 2025
@anordin95 anordin95 changed the title - Add an explainer guide for asyncio. Add an explainer guide for asyncio [gh-137026](https://github.com/python/cpython/issues/137026) Jul 29, 2025
@anordin95 anordin95 changed the title Add an explainer guide for asyncio [gh-137026](https://github.com/python/cpython/issues/137026) Add an explainer guide for asyncio [gh-137026] Jul 29, 2025
@AA-Turner AA-Turner changed the title Add an explainer guide for asyncio [gh-137026] gh-137026: Add an explainer guide for asyncio Jul 29, 2025
Copy link
Member

@StanFromIreland StanFromIreland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will review further once the lines are wrapped, as now it will invalidate suggestions.

Copy link
Member

@ZeroIntensity ZeroIntensity left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to commit to reviewing this if nobody else has the time. I think is generally an improvement to the documentation, but definitely needs some work still. Thanks for working on this!

Copy link
Member

@StanFromIreland StanFromIreland left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are still many minor infringements of the Style guide (e.g. simple English) which could be addressed in one pass.

You can also use Sphinx cross references when discussing specific keywords, functions etc.

Copy link
Member

@ZeroIntensity ZeroIntensity left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me. I've left a few grammar fixes as suggestions, but I'm comfortable enough with this to put the first approval on here.

I haven't read too deep into some of the technical discussions about asyncio internals, but I'm personally fine with keeping terms like "queue" in here. I really think we should focus on creating a HOWTO that's clear and easy to understand for beginners, not something that documents the exact inner workings of asyncio in detail.

This is based on the concept of lies-to-children ("a simplified, and often technically incorrect, explanation of technical or complex subjects employed as a teaching method"). This tutorial is targeted towards beginners, so it should be totally OK to use some information that's technically incorrect in the pursuit of initial comprehension.

For example, most schools initially teach you the concept of planetary electron shells, despite the fact that there are more correct (and complicated) models in quantum mechanics. We should not teach the quantum mechanics of asyncio here. Instead, our goal should be to spark curiosity in readers, even if the information is not as technically correct.

Thanks for all your hard work @anordin95!

@willingc
Copy link
Contributor

willingc commented Aug 7, 2025

This is based on the concept of lies-to-children ("a simplified, and often technically incorrect, explanation of technical or complex subjects employed as a teaching method"). This tutorial is targeted towards beginners, so it should be totally OK to use some information that's technically incorrect in the pursuit of initial comprehension.

Thanks @ZeroIntensity and @anordin95. HOWTO guides are not strictly targeted to beginners rather they are a deeper explanation of something. The name HOWTO comes from early Linux days as a deep dive into a particular area.

OK, we are down to one key decision: the best mental model for an event loop. We've been discussing a) the queue and b) the execution of scheduled work.

Event loops run asynchronous tasks and callbacks, perform network IO operations, and run subprocesses.

From the event loop docs, the above defines work run by an event loop. I'm going to suggest that instead of sticking with "queue" which doesn't sit well with multiple asyncio maintainers that we explore some other terms: "to do list", "work checklist", "collection of work".

Working from the conductor metaphor:

Like a symphony conductor, the event loop orchestrates multiple performers (tasks). It knows when each section should play, pauses some while others perform, and coordinates the overall harmony of concurrent operations.

@anordin95
Copy link
Author

anordin95 commented Aug 7, 2025

OK, we are down to one key decision: the best mental model for an event loop. We've been discussing a) the queue and b) the execution of scheduled work.

Like a symphony conductor, the event loop orchestrates multiple performers (tasks). It knows when each section should play, pauses some while others perform, and coordinates the overall harmony of concurrent operations.

This is a beautiful idea! But, I'm a bit wary of using it, since in a symphony many sections play truly concurrently. The trumpets, the drums & the flutes could all be going at once.

I reworked the event loop paragraph a decent bit (see below). The idea of a queue is mentioned only once now and is explicitly called out as a rough analogy twice in the sentence . Otherwise, I refer to the event loop's jobs with a variety of similar but different terms to help reinforce and contextualize the idea. Alongside this change, my idea also entails removing any other references to a queue in the broader article.


In more technical terms, the event loop contains a collection of jobs to be run. Some jobs are added directly by you, and some indirectly by asyncio. The event loop takes a job from its backlog of work and invokes it (or "gives it control"), similar to calling a function, and then that job runs. Once it pauses or completes, it returns control to the event loop. The event loop will then select another job from its pool and invoke it. You can roughly think of the collection of jobs as a queue: jobs are added and then processed one at a time, generally (but not always) in order. This process repeats indefinitely with the event loop cycling endlessly onwards. If there are no more jobs pending execution, the event loop is smart enough to rest and avoid needlessly wasting CPU cycles, and will come back when there's more work to be done.

@willingc
Copy link
Contributor

willingc commented Aug 7, 2025

Cool @anordin95! I like the last commit you pushed. 🎉 Can we continue throughout the doc to deemphasize queue?

@anordin95
Copy link
Author

anordin95 commented Aug 8, 2025

Can we continue throughout the doc to deemphasize queue?

Yep! Was just taking a little break. All references to queue besides the one in that first analogy are now gone.

@anordin95
Copy link
Author

anordin95 commented Aug 8, 2025

@ZeroIntensity, @willingc: It should be noted, I just added a blurb about Task garbage collection at the end of the "tasks" section. I was initially on the fence but I think it's important, useful context to include.

It's important to be aware that the task itself is not added to the event loop,
only a callback to the task is.
This matters if the task object you created is garbage collected before it's
called by the event loop.
For example, consider this program::
async def hello():
print("hello!")
async def main():
hello_task = asyncio.create_task(hello())
return
asyncio.run(main())
Because the coroutine ``main()`` exits before awaiting the task and no other
references to the task are made, the task object ``hello_task`` *might* be
garbage collected before the event loop invokes it.
That example still actually ends up running ``hello_task``, because
``asyncio`` and Python's garbage collector work pretty hard to ensure this
sort of thing doesn't happen.
But that's no reason to be reckless!

tree/main>`_ that inspired this HOWTO article, by Alexander Nordin.
* This in-depth `YouTube tutorial series <https://www.youtube.com/
watch?v=Xbl7XjFYsN4&list=PLhNSoGM2ik6SIkVGXWBwerucXjgP1rHmB>`_ on
``asyncio`` created by core Python team member, Łukasz Lang.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
``asyncio`` created by core Python team member, Łukasz Lang.
``asyncio`` created by core Python team member, Łukasz Langa.

That example highlights how using only ``await coroutine`` could
unintentionally hog control from other tasks and effectively stall the event
loop.
:func:`asyncio.run` can help you detect such occurences with the ``debug=True``
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, the way I'd expect most developers to use this is not by modifying the code, but by running python in dev mode locally (-X dev or PYTHONDEVMODE=1). A link should take users to: https://docs.python.org/3/library/asyncio-dev.html#asyncio-debug-mode

Once it pauses or completes, it returns control to the event loop.
The event loop will then select another job from its pool and invoke it.
You can *roughly* think of the collection of jobs as a queue: jobs are added and
then processed one at a time, generally (but not always) in order.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

generally (but not always) in order.

I still find this bit awkward as it's again suggesting that order of execution is unpredictable. Can we say that the jobs are processed in the order that they were scheduled or something?

Comment on lines +222 to +223
Because the coroutine ``main()`` exits before awaiting the task and no other
references to the task are made, the task object ``hello_task`` *might* be
Copy link
Contributor

@Dreamsorcerer Dreamsorcerer Aug 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This may not be the best example, as the application is expected to close at the end of main(). So the task gets cancelled anyway. i.e. Even if I keep a reference, I can get the task to not complete:

>>> async def hello():
...  await asyncio.sleep(1)
...  print("BAR")
... 
>>> async def main():
...  global t
...  t = asyncio.create_task(hello())
... 
>>> asyncio.run(main())
>>> t
<Task cancelled name='Task-10' coro=<hello() done, defined at <stdin>:1>>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Todo
Development

Successfully merging this pull request may close these issues.

9 participants