#+TITLE: AsyncIO In Depth
If you find Python's async / await to be mostly easy to use, yet
somewhat magical and opaque I think you'll find this series of
articles valuable. I spent several weeks digging into asyncio's
internals and these articles are the result of my exploration.
They aren't a way to quickly become productive with asyncio: this
series is the one you should read after you can write a little bit of
async code, but aren't quite sure how or why things work.
This is an in-progress depth-first-traversal into the Python's
event loop implementation. After several aborted attempts at writing
about asyncio in 2020, I'm publishing this as a series in pieces
instead of trying to complete the full and then sharing.
Part of my exploration last year also involved building and open
sourcing Panopticon -- a Python tracer to visualize async execution,
so I'll also touch upon that along the way, with some other projects I
have in mind.
As far as possible, I'll also be describing how I went about
spelunking into asyncio along the way as well.
#+CAPTION: Several attempts at writing about AsyncIO
#+begin_src sh :results output verbatim
tree ~/expLog/src/drafts/asyncio
#+end_src
#+RESULTS:
#+begin_example
/home/knl/expLog/src/drafts/asyncio
├── asyncio
│ ├── eventloops.org
│ └── index.org
├── asyncio.org
├── asyncioscheduler.org
├── asyncioterms.org
├── eventloop.org
├── python.org
└── tracingasyncio.org
1 directory, 8 files
#+end_example
: I have the infinitesimally smallest possible inkling of
what Don Knuth's life must be like.
* Hello, world!
I'll use a classical program and make it slightly more interesting and
asynchronous, to have a simple program to dissect throughout the
series.
There's nothing very complex here: I just tweaked our favorite program
to simulate a reasonably proficient typist.
../static/images/hello.gif
#+begin_src python :results output
import asyncio
import random
async def hello_world():
for ch in "Hello, world!":
x = max(0, random.gauss(.08, .03))
await asyncio.sleep(x)
print(ch, end="", flush=True)
print()
asyncio.run(hello_world())
#+end_src
#+RESULTS:
: Hello, world!
For comparison, I'll also contrast it against a far more boring and
synchronous version of the program:
#+begin_src python :results output
import random
import time
def sync_hello_world():
for ch in "Hello, world!":
x = max(0, random.gauss(.08, .03))
time.sleep(x)
print(ch, end="", flush=True)
print()
sync_hello_world()
#+end_src
#+RESULTS:
: Hello, world!
* Tentative Table of Contents
** async
** Coroutine objects
** await
** asyncio
*** asyncio.sleep
** The Event loop
** The Scheduler
** epoll
** Generators
** Async Generators
** Coroutine classes
** Design patterns