LONG ROAD
ANDREW GODWIN // @andrewgodwin
ASYNCHRONY
TO
THE
Slide 2
Slide 2 text
Andrew Godwin / @andrewgodwin
Hi, I’m
Andrew Godwin
• Django core developer
• Worked on Migrations, Channels & Async
• Once a Londoner, now from Denver, USA
Slide 3
Slide 3 text
Andrew Godwin / @andrewgodwin
Slide 4
Slide 4 text
Andrew Godwin / @andrewgodwin
"Asynchronous Programming"
What is it, really?
Slide 5
Slide 5 text
Andrew Godwin / @andrewgodwin
Concurrent Programming
The more general term
Slide 6
Slide 6 text
Andrew Godwin / @andrewgodwin
Input
Process
Output
Sequential execution
Slide 7
Slide 7 text
Andrew Godwin / @andrewgodwin
Input
Process
Output
Process
Concurrent execution
Archive
Slide 8
Slide 8 text
Andrew Godwin / @andrewgodwin
Shared use of a single resource
In this case, CPUs
Slide 9
Slide 9 text
Andrew Godwin / @andrewgodwin
"Communicating Sequential Processes", C. A. R Hoare
Andrew Godwin / @andrewgodwin
Multiple processes scale best
It's also difficult and costs the most!
Slide 13
Slide 13 text
Andrew Godwin / @andrewgodwin
Threads are unpredictable
Also, the GIL is our ever-present friend
Slide 14
Slide 14 text
Andrew Godwin / @andrewgodwin
Event loops are a good compromise
They do require shared memory, though.
Slide 15
Slide 15 text
Andrew Godwin / @andrewgodwin
Asynchronous ≈ Event loops
Most of the time!
Slide 16
Slide 16 text
Andrew Godwin / @andrewgodwin
# Ready when a timer finishes
await asyncio.sleep(1)
# Ready when network packets return
await client.get("http://example.com")
# Ready when the coroutine exits
await my_function("hello", 64.2)
Slide 17
Slide 17 text
Andrew Godwin / @andrewgodwin
Network/timer updates
An event loop's flow
Select a ready task
Run task
Add new tasks to queue
await
Slide 18
Slide 18 text
Andrew Godwin / @andrewgodwin
Coroutines
Time →
Slide 19
Slide 19 text
Andrew Godwin / @andrewgodwin
How did we get here?
Andrew Godwin / @andrewgodwin
2017 Django Channels 1.0
2018 Django Channels 2.0
2019 DEP 9 (Async support)
2020 Async views land in Django
Slide 22
Slide 22 text
Andrew Godwin / @andrewgodwin
No solution is perfect
Everyone chooses different tradeoffs
Slide 23
Slide 23 text
Andrew Godwin / @andrewgodwin
Asyncio is based on yield from
Because it was prototyped in Python 2
Slide 24
Slide 24 text
Andrew Godwin / @andrewgodwin
Can't tell if a function returns a coroutine!
There are standard hints, but no actual guaranteed way
Slide 25
Slide 25 text
Andrew Godwin / @andrewgodwin
async def calculate(x):
result = await coroutine(x)
return result
# These both return a coroutine
def calculate(x):
result = coroutine(x)
return result
Slide 26
Slide 26 text
Andrew Godwin / @andrewgodwin
Can't have one function service both
How we got here makes sense, but it's still annoying sometimes.
Slide 27
Slide 27 text
Andrew Godwin / @andrewgodwin
# Calls get.__call__
instance = MyModel.objects.get(id=3)
# Calls get.__call__
# and then awaits its result
instance = await MyModel.objects.get(id=3)
Slide 28
Slide 28 text
Andrew Godwin / @andrewgodwin
You have to namespace async functions
I really, really wish we didn't have to