ASYNCHRONOUS
PROGRAMMING IN PYTHON
P R E S E N T, PA S T A N D F U T U R E O F
www.prontotools.io
Y O T H I N M U A N G S O M M U K
Y O T H I N
M U A N G S O M M U K
A B O U T M E
Pythonista
Best in class productivity tools that enable smart and
efficient marketing execution for small business
B e t t e r M a r k e t i n g V i s i b i l i t y
A S Y N C
A programming technique where execution order is not
known ahead of time
W H Y W E N E E D T O K N O W A S Y N C
P R O G R A M M I N G
B E C A U S E I T M A K E Y O U R C O D E
G O FA S T
H O W FA S T
B O B B Y F I S C H E R P L AY I N G 5 0 O P P O N E N T S S I M U LTA N E O U S LY, 1 9 6 4
S Y N C H R O N O U S C H E S S
E X H I B I T I O N
- 50 opponents
- Fischer move in 5 seconds
- Opponents move in 55 seconds
- Game average 30 move pairs
Assumptions:
Each game runs for 30 minutes
50 sequential games would take
50 x 30 min = 1500 min = 25 hours
B O B B Y F I S C H E R P L AY I N G 5 0 O P P O N E N T S S I M U LTA N E O U S LY, 1 9 6 4
A S Y N C H R O N O U S C H E S S
E X H I B I T I O N
- Fischer moves on first game
- While opponent thinks, he moves

on second game, the third, fourth…
- A move on all 50 game takes him

50 x 5 sec = 250 sec = 4 min
- After he complete the round, the 

first game is ready for his next move
- 50 games are completed in

4 min x 30 = 120 min = 2 hour
C O R O U T I N E
– W I K I P E D I A
C o r o u t i n e s a r e c o m p u t e r p r o g r a m
components that generalize subroutines for
nonpreemptive multitasking, by allowing
multiple entry points for suspending and
resuming execution at certain locations.
coroutines are functions whose execution
you can pause
E V E N T L O O P
– W I K I P E D I A
Event loop is a programming construct that
waits for a dispatches events or messages
in a program.
A B R I E F H I S T O RY
S I M P L E G E N E R AT O R
P Y T H O N 2 . 2 ( P E P 2 5 5 ) - 1 8 M AY 2 0 0 1
Generator was born with
“yield” expression make us
able to generate iterator
without holding memory
upfront, all you need is
memory for the current value
def eager_range(up_to):
sequence = []
index = 0
while index < up_to:
sequence.append(index)
Index += 1
return sequence
S I M P L E G E N E R AT O R
P Y T H O N 2 . 2 ( P E P 2 5 5 ) - 1 8 M AY 2 0 0 1
Generator was born with
“yield” expression make us
able to generate iterator
without holding memory
upfront, all you need is
memory for the current value
def lazy_range(up_to):
index = 0
while index < up_to:
yield index
Index += 1
C O R O U T I N E S V I A
E N H A N C E D G E N E R AT O R S
P Y T H O N 2 . 5 ( P E P 3 4 2 ) - 1 0 M AY 2 0 0 5
“send()” method on
generator, that allowed us
not only pause the
generator, but also send
back the value to generator
def jumping_range(up_to):
index = 0
while index < up_to:
jump = yield index
if jump is None:
jump = 1
index += jump
if __name__ == '__main__':
iterator = jumping_range(5)
print(next(iterator)) # 0
print(iterator.send(2)) # 2
print(next(iterator)) # 3
S Y N TA X F O R D E L E G AT I N G T O
A S U B G E N E R AT O R
P Y T H O N 3 . 3 ( P E P 3 8 0 ) - 1 3 F E B 2 0 0 9
“yield from” expression for yield every
value from iterator (generator) also able to
chain generator together which made us
able to bubble up and down the call stack
without code having to do anything special
def lazy_range(up_to):
index = 0
def gratuitous_refactor():
nonlocal index
while index < up_to:
yield index
index += 1
yield from gratuitous_refactor()
S Y N TA X F O R D E L E G AT I N G T O
A S U B G E N E R AT O R
P Y T H O N 3 . 3 ( P E P 3 8 0 ) - 1 3 F E B 2 0 0 9
“yield from” expression for yield every
value from iterator (generator) also able to
chain generator together which made us
able to bubble up and down the call stack
without code having to do anything special
def bottom():
return (yield 42)
def middle():
return (yield from bottom())
def top():
return (yield from middle())
gen = top()
value = next(gen)
print(value) # 42
P Y T H O N 3 . 4 ( P E P 3 1 5 6 ) - 1 2 D E C 2 0 1 2
Pluggable event loop made non blocking
IO happen, but not actual asynchronous
programming. It’s concurrent programming
A S Y N C I O
import asyncio
loop = asyncio.get_event_loop()
@asyncio.coroutine
def hello():
print('Hello')
yield from asyncio.sleep(3)
print('World!')
if __name__ == '__main__':
loop.run_until_complete(hello())
A S Y N C A WA I T
P Y T H O N 3 . 5 ( P E P 4 9 2 ) - 9 A P R 2 0 1 5
Explicit asynchronous code there is
no need to do “asyncio.coroutine”
decorator with generator that return
“yield from” anymore. Using
“async def” syntax to define that
function is a coroutine and use
“await” or “return” for delegate
the results like “yield from”
import asyncio
loop = asyncio.get_event_loop()
async def hello():
print('Hello')
await asyncio.sleep(3)
print('World!')
if __name__ == '__main__':
loop.run_until_complete(hello())
import asyncio
async def compute(x, y):
print(“Compute %s + %s …” % (x, y))
await asyncio.sleep(1.0)
return x + y
async def print_sum(x, y):
result = await compute(x, y)
print(“%s + %s = %s” % (x, y, result))
loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()
A S Y N C A WA I T
P Y T H O N 3 . 5 ( P E P 4 9 2 ) - 9 A P R 2 0 1 5
Explicit asynchronous code there is
no need to do “asyncio.coroutine”
decorator with generator that return
“yield from” anymore. Using
“async def” syntax to define that
function is a coroutine and use
“await” or “return” for delegate
the results like “yield from”
ref: https://docs.python.org/3/library/asyncio-task.html
A S Y N C G E N E R AT O R
P Y T H O N 3 . 6 ( P E P 5 2 5 ) - 2 8 J U L 2 0 1 6
Remove restriction to use await and yield
in the same function body
import asyncio
loop = asyncio.get_event_loop()
async def ticker(delay, to):
for i in range(to)
yield i
await asyncio.sleep(delay)
if __name__ == '__main__':
loop.run_until_complete(ticker(5, 10))
A S Y N C
C O M P R E H E N S I O N S
P Y T H O N 3 . 6 ( P E P 5 3 0 ) - 3 S E P 2 0 1 6
Introduce async for and await expression
in all kinds of comprehensions
result = [ i async for i in aiter() if i % 2 ]
result = [ await fun() for fun in funcs if await condition() ]
E V E N T L O O P
P L U G G A B L E
U V L O O P
import asyncio
import uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
H T T P B E N C H M A R K S
▄▄▄▄▄
▀▀▀██████▄▄▄ _______________
▄▄▄▄▄ █████████▄ / 
▀▀▀▀█████▌ ▀▐▄ ▀▐█ | Gotta go fast! |
▀▀█████▄▄ ▀██████▄██ | _________________/
▀▄▄▄▄▄ ▀▀█▄▀█════█▀ |/
▀▀▀▄ ▀▀███ ▀ ▄▄
▄███▀▀██▄████████▄ ▄▀▀▀▀▀▀█▌
██▀▄▄▄██▀▄███▀ ▀▀████ ▄██
▄▀▀▀▄██▄▀▀▌████▒▒▒▒▒▒███ ▌▄▄▀
▌ ▐▀████▐███▒▒▒▒▒▐██▌
▀▄▄▄▄▀ ▀▀████▒▒▒▒▄██▀
▀▀█████████▀
▄▄██▀██████▀█
▄██▀ ▀▀▀ █
▄█ ▐▌
▄▄▄▄█▌ ▀█▄▄▄▄▀▀▄
▌ ▐ ▀▀▄▄▄▀
▀▀▄▄▀
S A N I C
from sanic import Sanic
from sanic.response import son
app = Sanic()
@app.route(“/“)
async def test(request):
return json({“Hello”: “world”})
if __name__ == “__main__”:
app.run(host=“0.0.0.0”, port=8000)
J A P R O N T O
H T T P S : / / M E D I U M . F R E E C O D E C A M P. C O M / M I L L I O N - R E Q U E S T S - P E R - S E C O N D - W I T H - P Y T H O N - 9 5 C 1 3 7 A F 3 1 9
J A P R O N T O
J A P R O N T O
import asyncio
from japronto import Application
app = Application()
async def asynchronous(request):
for i in range(1, 4):
await asyncio.sleep(1)
print(i, ‘seconds elapsed’)
return request.Response(text=‘3 sec elapsed’)
r = app.router
r.add_route(‘/async’, asynchronous)
app.run()
T O K I O
A LT E R N AT I V E
C U R I O
C U R I O
- Using same async / await syntactic
- Different approach from any other I / O Libraries
- Simplicity
O T H E R L I B R A R I E S
- aiohttp
- asyncpg
- aiosmtp
- aio.testing
- aioelasticsearch
- etc.
Questions?
R E F E R E N C E
- https://snarky.ca/how-the-heck-does-async-await-work-in-python-3-5/
- https://hackernoon.com/asynchronous-python-45df84b82434
- https://magic.io/blog/uvloop-blazing-fast-python-networking/
- https://github.com/channelcat/sanic
- https://medium.freecodecamp.com/million-requests-per-second-with-python-95c137af319
- https://github.com/PyO3/tokio
- Miguel Grinberg Asynchronous Python for the Complete Beginner PyCon 2017
- Yury Selivanov asyncawait and asyncio in Python 3 6 and beyond PyCon 2017
We are hiring!
https://www.prontomarketing.com/company/careers/

Codemania101: The Present, Past and Future of Asynchronous Programming in Python

  • 1.
    ASYNCHRONOUS PROGRAMMING IN PYTHON PR E S E N T, PA S T A N D F U T U R E O F www.prontotools.io Y O T H I N M U A N G S O M M U K
  • 2.
    Y O TH I N M U A N G S O M M U K A B O U T M E Pythonista
  • 5.
    Best in classproductivity tools that enable smart and efficient marketing execution for small business
  • 6.
    B e tt e r M a r k e t i n g V i s i b i l i t y
  • 8.
    A S YN C
  • 9.
    A programming techniquewhere execution order is not known ahead of time
  • 10.
    W H YW E N E E D T O K N O W A S Y N C P R O G R A M M I N G
  • 11.
    B E CA U S E I T M A K E Y O U R C O D E G O FA S T
  • 12.
    H O WFA S T
  • 13.
    B O BB Y F I S C H E R P L AY I N G 5 0 O P P O N E N T S S I M U LTA N E O U S LY, 1 9 6 4 S Y N C H R O N O U S C H E S S E X H I B I T I O N - 50 opponents - Fischer move in 5 seconds - Opponents move in 55 seconds - Game average 30 move pairs Assumptions: Each game runs for 30 minutes 50 sequential games would take 50 x 30 min = 1500 min = 25 hours
  • 14.
    B O BB Y F I S C H E R P L AY I N G 5 0 O P P O N E N T S S I M U LTA N E O U S LY, 1 9 6 4 A S Y N C H R O N O U S C H E S S E X H I B I T I O N - Fischer moves on first game - While opponent thinks, he moves
 on second game, the third, fourth… - A move on all 50 game takes him
 50 x 5 sec = 250 sec = 4 min - After he complete the round, the 
 first game is ready for his next move - 50 games are completed in
 4 min x 30 = 120 min = 2 hour
  • 15.
    C O RO U T I N E
  • 16.
    – W IK I P E D I A C o r o u t i n e s a r e c o m p u t e r p r o g r a m components that generalize subroutines for nonpreemptive multitasking, by allowing multiple entry points for suspending and resuming execution at certain locations.
  • 17.
    coroutines are functionswhose execution you can pause
  • 18.
    E V EN T L O O P
  • 19.
    – W IK I P E D I A Event loop is a programming construct that waits for a dispatches events or messages in a program.
  • 20.
    A B RI E F H I S T O RY
  • 21.
    S I MP L E G E N E R AT O R P Y T H O N 2 . 2 ( P E P 2 5 5 ) - 1 8 M AY 2 0 0 1 Generator was born with “yield” expression make us able to generate iterator without holding memory upfront, all you need is memory for the current value def eager_range(up_to): sequence = [] index = 0 while index < up_to: sequence.append(index) Index += 1 return sequence
  • 22.
    S I MP L E G E N E R AT O R P Y T H O N 2 . 2 ( P E P 2 5 5 ) - 1 8 M AY 2 0 0 1 Generator was born with “yield” expression make us able to generate iterator without holding memory upfront, all you need is memory for the current value def lazy_range(up_to): index = 0 while index < up_to: yield index Index += 1
  • 23.
    C O RO U T I N E S V I A E N H A N C E D G E N E R AT O R S P Y T H O N 2 . 5 ( P E P 3 4 2 ) - 1 0 M AY 2 0 0 5 “send()” method on generator, that allowed us not only pause the generator, but also send back the value to generator def jumping_range(up_to): index = 0 while index < up_to: jump = yield index if jump is None: jump = 1 index += jump if __name__ == '__main__': iterator = jumping_range(5) print(next(iterator)) # 0 print(iterator.send(2)) # 2 print(next(iterator)) # 3
  • 24.
    S Y NTA X F O R D E L E G AT I N G T O A S U B G E N E R AT O R P Y T H O N 3 . 3 ( P E P 3 8 0 ) - 1 3 F E B 2 0 0 9 “yield from” expression for yield every value from iterator (generator) also able to chain generator together which made us able to bubble up and down the call stack without code having to do anything special def lazy_range(up_to): index = 0 def gratuitous_refactor(): nonlocal index while index < up_to: yield index index += 1 yield from gratuitous_refactor()
  • 25.
    S Y NTA X F O R D E L E G AT I N G T O A S U B G E N E R AT O R P Y T H O N 3 . 3 ( P E P 3 8 0 ) - 1 3 F E B 2 0 0 9 “yield from” expression for yield every value from iterator (generator) also able to chain generator together which made us able to bubble up and down the call stack without code having to do anything special def bottom(): return (yield 42) def middle(): return (yield from bottom()) def top(): return (yield from middle()) gen = top() value = next(gen) print(value) # 42
  • 26.
    P Y TH O N 3 . 4 ( P E P 3 1 5 6 ) - 1 2 D E C 2 0 1 2 Pluggable event loop made non blocking IO happen, but not actual asynchronous programming. It’s concurrent programming A S Y N C I O import asyncio loop = asyncio.get_event_loop() @asyncio.coroutine def hello(): print('Hello') yield from asyncio.sleep(3) print('World!') if __name__ == '__main__': loop.run_until_complete(hello())
  • 27.
    A S YN C A WA I T P Y T H O N 3 . 5 ( P E P 4 9 2 ) - 9 A P R 2 0 1 5 Explicit asynchronous code there is no need to do “asyncio.coroutine” decorator with generator that return “yield from” anymore. Using “async def” syntax to define that function is a coroutine and use “await” or “return” for delegate the results like “yield from” import asyncio loop = asyncio.get_event_loop() async def hello(): print('Hello') await asyncio.sleep(3) print('World!') if __name__ == '__main__': loop.run_until_complete(hello())
  • 28.
    import asyncio async defcompute(x, y): print(“Compute %s + %s …” % (x, y)) await asyncio.sleep(1.0) return x + y async def print_sum(x, y): result = await compute(x, y) print(“%s + %s = %s” % (x, y, result)) loop = asyncio.get_event_loop() loop.run_until_complete(print_sum(1, 2)) loop.close() A S Y N C A WA I T P Y T H O N 3 . 5 ( P E P 4 9 2 ) - 9 A P R 2 0 1 5 Explicit asynchronous code there is no need to do “asyncio.coroutine” decorator with generator that return “yield from” anymore. Using “async def” syntax to define that function is a coroutine and use “await” or “return” for delegate the results like “yield from”
  • 29.
  • 30.
    A S YN C G E N E R AT O R P Y T H O N 3 . 6 ( P E P 5 2 5 ) - 2 8 J U L 2 0 1 6 Remove restriction to use await and yield in the same function body import asyncio loop = asyncio.get_event_loop() async def ticker(delay, to): for i in range(to) yield i await asyncio.sleep(delay) if __name__ == '__main__': loop.run_until_complete(ticker(5, 10))
  • 31.
    A S YN C C O M P R E H E N S I O N S P Y T H O N 3 . 6 ( P E P 5 3 0 ) - 3 S E P 2 0 1 6 Introduce async for and await expression in all kinds of comprehensions result = [ i async for i in aiter() if i % 2 ] result = [ await fun() for fun in funcs if await condition() ]
  • 32.
    E V EN T L O O P P L U G G A B L E
  • 33.
    U V LO O P
  • 34.
  • 35.
    ▄▄▄▄▄ ▀▀▀██████▄▄▄ _______________ ▄▄▄▄▄ █████████▄/ ▀▀▀▀█████▌ ▀▐▄ ▀▐█ | Gotta go fast! | ▀▀█████▄▄ ▀██████▄██ | _________________/ ▀▄▄▄▄▄ ▀▀█▄▀█════█▀ |/ ▀▀▀▄ ▀▀███ ▀ ▄▄ ▄███▀▀██▄████████▄ ▄▀▀▀▀▀▀█▌ ██▀▄▄▄██▀▄███▀ ▀▀████ ▄██ ▄▀▀▀▄██▄▀▀▌████▒▒▒▒▒▒███ ▌▄▄▀ ▌ ▐▀████▐███▒▒▒▒▒▐██▌ ▀▄▄▄▄▀ ▀▀████▒▒▒▒▄██▀ ▀▀█████████▀ ▄▄██▀██████▀█ ▄██▀ ▀▀▀ █ ▄█ ▐▌ ▄▄▄▄█▌ ▀█▄▄▄▄▀▀▄ ▌ ▐ ▀▀▄▄▄▀ ▀▀▄▄▀ S A N I C from sanic import Sanic from sanic.response import son app = Sanic() @app.route(“/“) async def test(request): return json({“Hello”: “world”}) if __name__ == “__main__”: app.run(host=“0.0.0.0”, port=8000)
  • 36.
    J A PR O N T O
  • 37.
    H T TP S : / / M E D I U M . F R E E C O D E C A M P. C O M / M I L L I O N - R E Q U E S T S - P E R - S E C O N D - W I T H - P Y T H O N - 9 5 C 1 3 7 A F 3 1 9 J A P R O N T O
  • 38.
    J A PR O N T O import asyncio from japronto import Application app = Application() async def asynchronous(request): for i in range(1, 4): await asyncio.sleep(1) print(i, ‘seconds elapsed’) return request.Response(text=‘3 sec elapsed’) r = app.router r.add_route(‘/async’, asynchronous) app.run()
  • 39.
    T O KI O
  • 41.
    A LT ER N AT I V E
  • 42.
    C U RI O
  • 43.
    C U RI O - Using same async / await syntactic - Different approach from any other I / O Libraries - Simplicity
  • 44.
    O T HE R L I B R A R I E S
  • 45.
    - aiohttp - asyncpg -aiosmtp - aio.testing - aioelasticsearch - etc.
  • 46.
  • 47.
    R E FE R E N C E - https://snarky.ca/how-the-heck-does-async-await-work-in-python-3-5/ - https://hackernoon.com/asynchronous-python-45df84b82434 - https://magic.io/blog/uvloop-blazing-fast-python-networking/ - https://github.com/channelcat/sanic - https://medium.freecodecamp.com/million-requests-per-second-with-python-95c137af319 - https://github.com/PyO3/tokio - Miguel Grinberg Asynchronous Python for the Complete Beginner PyCon 2017 - Yury Selivanov asyncawait and asyncio in Python 3 6 and beyond PyCon 2017
  • 48.