14.2. Async Introduction

  • asyncio in Python standard library

  • async and await builtin keywords

  • Running asynchronously: 3s + 1s + 1s = bit over 3s [execution time]

  • Async is the future of programming

../../_images/about-processing-sequence.png
../../_images/about-processing-anatomy-1.png
../../_images/about-processing-anatomy-2.png

14.2.1. Advantages

  • Maximize the usage of a single thread

  • Handling I/O asynchronously

  • Enabling concurrent code using coroutines

  • Async will fill the gaps, otherwise wasted on waiting for I/O

  • You control when tasks switches occur, so locks and other synchronization are no longer needed

  • Async is the cheapest way to task switch

  • Cost task switches is incredibly low; calling a pure Python function has more overhead than restarting a generator or awaitable

  • Function builds stack each time it's called, whereas async uses generators underneath, which already has stack created

  • In terms of speed async servers blows threaded servers in means of thousands

  • Async is very cheap in means of resources

  • Async world has a huge ecosystem of support tools

  • Coding is easier to get right, than threads

14.2.2. Disadvantages

  • Async switches cooperatively, so you do need to add explicit code yield or await to cause a task to switch

  • Everything you do need a non-blocking version (for example open())

  • Increased learning curve

  • Create event loop, acquire, crate non-blocking versions of your code

  • You think you know Python, there is a second half to learn (async)

14.2.3. Sync vs. Async

../../_images/about-processing-sync.png
../../_images/about-processing-timetoresponse.png
../../_images/about-processing-waittime.png
../../_images/concurrency-sync-vs-async-1.png

Figure 14.1. Source: Langa, Ł. import asyncio: Learn Python's AsyncIO [4]

../../_images/concurrency-sync-vs-async-2.png

Figure 14.2. Source: Langa, Ł. import asyncio: Learn Python's AsyncIO [4]

14.2.4. Execution

../../_images/about-processing-anatomy-1.png
../../_images/about-processing-anatomy-2.png

14.2.5. Example

>>> import asyncio
>>>
>>>
>>> async def a():
...     print('a: started')
...     await asyncio.sleep(0.2)
...     print('a: finished')
...     return 'a'
>>>
>>> async def b():
...     print('b: started')
...     await asyncio.sleep(0.1)
...     print('b: finished')
...     return 'b'
>>>
>>> async def c():
...     print('c: started')
...     await asyncio.sleep(0.3)
...     print('c: finished')
...     return 'c'
>>>
>>> async def main():
...     result = await asyncio.gather(a(), b(), c())
...     print(f'Result: {result}')
>>>
>>>
>>> asyncio.run(main())
a: started
b: started
c: started
b: finished
a: finished
c: finished
Result: ['a', 'b', 'c']

14.2.6. Further Reading

  • Kennedy, M. Demystifying Python's Async and Await Keywords [1]

  • Kennedy, M. Async Techniques and Examples in Python [2]

  • Abdalla, A. Creating a Bittorrent Client using Asyncio [3]

  • Langa, Ł. import asyncio: Learn Python's AsyncIO [4]

14.2.7. References

14.2.8. Assignments

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: OOP Async Coroutine
# - Difficulty: easy
# - Lines: 2
# - Minutes: 2

# %% English
# 1. Define coroutine function `a()`
# 2. After running coroutine should return 'a'
# 3. Run doctests - all must succeed

# %% Polish
# 1. Zdefiniuj coroutine function `a()`
# 2. Po uruchomieniu coroutine powinna zwracać 'a'
# 3. Uruchom doctesty - wszystkie muszą się powieść

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 10), \
'Python 3.10+ required'

>>> import sys; sys.tracebacklimit = 0
>>> from inspect import iscoroutine, iscoroutinefunction
>>> import asyncio

>>> assert iscoroutinefunction(a)
>>> assert iscoroutine(a())

>>> asyncio.run(a())
'a'
"""


# Define coroutine function `a()`
# After running coroutine should return 'a'
# type: Coroutine
def a():
    ...


# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: OOP Async Sleep
# - Difficulty: easy
# - Lines: 3
# - Minutes: 2

# %% English
# 1. Define coroutine function `a()`
# 2. After running coroutine should:
#    - wait for 1.0 seconds
#    - return 'a'
# 3. Use function `sleep()` from `asyncio` module
# 4. Run doctests - all must succeed

# %% Polish
# 1. Zdefiniuj coroutine function `a()`
# 2. Po uruchomieniu coroutine powinna:
#    - czekać 1.0 sekundę
#    - zwracać 'a'
# 3. Użyj funkcji `sleep()` z modułu `asyncio`
# 4. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `asyncio.sleep()`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 10), \
'Python 3.10+ required'

>>> from inspect import iscoroutine, iscoroutinefunction
>>> import asyncio

>>> assert iscoroutinefunction(a)
>>> assert iscoroutine(a())

>>> asyncio.run(a())
'a'
"""

import asyncio

# coroutine function `a()`
# wait for 1.0 seconds, return 'a'
# type: Coroutine
def a():
    ...


# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: OOP Async Concurrent
# - Difficulty: easy
# - Lines: 3
# - Minutes: 2

# %% English
# 1. Define coroutine function `a()`
#    which after running should
#       - print: 'a: before'
#       - sleep: 1.0 second
#       - print: 'a: after'
# 2. Define coroutine function `b()`
#    which after running should
#       - print: 'b: before'
#       - sleep: 0.5 second
#       - print: 'b: after'
# 3. Define coroutine function `c()`
#    which after running should
#       - print: 'c: before'
#       - sleep: 1.5 second
#       - print: 'c: after'
# 4. Use function `time.sleep()`
# 5. Do not use `await` keyword in front of `sleep`
# 6. Run doctests - all must succeed

# %% Polish
# 1. Zdefiniuj coroutine function `a()`
#    która po uruchomieniu powinna
#       - wypisać: 'a: before'
#       - czekać: 1.0 sekundy
#       - wypisać: 'a: after'
# 2. Zdefiniuj coroutine function `b()`
#    która po uruchomieniu powinna
#       - wypisać: 'b: before'
#       - czekać: 0.5 sekundy
#       - wypisać: 'b: after'
# 3. Zdefiniuj coroutine function `c()`
#    która po uruchomieniu powinna
#       - wypisać: 'c: before'
#       - czekać: 1.5 sekundy
#       - wypisać: 'c: after'
# 4. Użyj funkcji `time.sleep()`
# 5. Użyj słowa kluczowego `await` przed `sleep`
# 6. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `time.sleep()`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 10), \
'Python 3.10+ required'

>>> from inspect import iscoroutine, iscoroutinefunction
>>> import asyncio

>>> assert iscoroutinefunction(a)
>>> assert iscoroutinefunction(b)
>>> assert iscoroutinefunction(c)
>>> assert iscoroutine(a())
>>> assert iscoroutine(b())
>>> assert iscoroutine(c())

>>> async def main():
...     return await asyncio.gather(a(), b(), c())
>>>
>>> result = asyncio.run(main())
a: before
a: after
b: before
b: after
c: before
c: after
"""

import asyncio
import time

# Define coroutine function `a()`
# which after running should
# - print: 'a: before'
# - sleep: 1.0 second
# - print: 'a: after'
# Use function `time.sleep()`
# Do not use `await` keyword in front of `sleep`
# type: Coroutine
async def a():
    print('a: before')
    ...
    print('a: after')

# Define coroutine function `b()`
# which after running should
# - print: 'b: before'
# - sleep: 0.5 second
# - print: 'b: after'
# Use function `time.sleep()`
# Do not use `await` keyword in front of `sleep`
# type: Coroutine
async def b():
    print('b: before')
    ...
    print('b: after')

# Define coroutine function `c()`
# which after running should
# - print: 'c: before'
# - sleep: 1.5 second
# - print: 'c: after'
# Use function `time.sleep()`
# Do not use `await` keyword in front of `sleep`
# type: Coroutine
async def c():
    print('c: before')
    ...
    print('c: after')


# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: OOP Async Concurrent
# - Difficulty: easy
# - Lines: 3
# - Minutes: 2

# %% English
# 1. Define coroutine function `a()`
#    which after running should
#       - print: 'a: before'
#       - sleep: 1.0 second
#       - print: 'a: after'
# 2. Define coroutine function `b()`
#    which after running should
#       - print: 'b: before'
#       - sleep: 0.5 second
#       - print: 'b: after'
# 3. Define coroutine function `c()`
#    which after running should
#       - print: 'c: before'
#       - sleep: 1.5 second
#       - print: 'c: after'
# 4. Use function `asyncio.sleep()`
# 5. Do not use `await` keyword in front of `sleep`
# 6. Run doctests - all must succeed

# %% Polish
# 1. Zdefiniuj coroutine function `a()`
#    która po uruchomieniu powinna
#       - wypisać: 'a: before'
#       - czekać: 1.0 sekundy
#       - wypisać: 'a: after'
# 2. Zdefiniuj coroutine function `b()`
#    która po uruchomieniu powinna
#       - wypisać: 'b: before'
#       - czekać: 0.5 sekundy
#       - wypisać: 'b: after'
# 3. Zdefiniuj coroutine function `c()`
#    która po uruchomieniu powinna
#       - wypisać: 'c: before'
#       - czekać: 1.5 sekundy
#       - wypisać: 'c: after'
# 4. Użyj funkcji `asyncio.sleep()`
# 5. Nie używaj słowa kluczowego `await` przed `sleep`
# 6. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `asyncio.sleep()`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 10), \
'Python 3.10+ required'

>>> from inspect import iscoroutine, iscoroutinefunction
>>> import asyncio

>>> assert iscoroutinefunction(a)
>>> assert iscoroutinefunction(b)
>>> assert iscoroutinefunction(c)
>>> assert iscoroutine(a())
>>> assert iscoroutine(b())
>>> assert iscoroutine(c())

>>> async def main():
...     return await asyncio.gather(a(), b(), c())
>>>
>>> result = asyncio.run(main())
a: before
a: after
b: before
b: after
c: before
c: after
"""

import asyncio

# Define coroutine function `a()`
# which after running should
# - print: 'a: before'
# - sleep: 1.0 second
# - print: 'a: after'
# Use function `asyncio.sleep()`
# Do not use `await` keyword in front of `sleep`
# type: Coroutine
async def a():
    print('a: before')
    ...
    print('a: after')

# Define coroutine function `b()`
# which after running should
# - print: 'b: before'
# - sleep: 0.5 second
# - print: 'b: after'
# Use function `asyncio.sleep()`
# Do not use `await` keyword in front of `sleep`
# type: Coroutine
async def b():
    print('b: before')
    ...
    print('b: after')

# Define coroutine function `c()`
# which after running should
# - print: 'c: before'
# - sleep: 1.5 second
# - print: 'c: after'
# Use function `asyncio.sleep()`
# Do not use `await` keyword in front of `sleep`
# type: Coroutine
async def c():
    print('c: before')
    ...
    print('c: after')


# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: OOP Async Concurrent
# - Difficulty: easy
# - Lines: 3
# - Minutes: 2

# %% English
# 1. Define coroutine function `a()`
#    which after running should
#       - print: 'a: before'
#       - sleep: 1.0 second
#       - print: 'a: after'
# 2. Define coroutine function `b()`
#    which after running should
#       - print: 'b: before'
#       - sleep: 0.5 second
#       - print: 'b: after'
# 3. Define coroutine function `c()`
#    which after running should
#       - print: 'c: before'
#       - sleep: 1.5 second
#       - print: 'c: after'
# 4. Use function `asyncio.sleep()`
# 5. Use `await` keyword in front of `sleep`
# 6. Run doctests - all must succeed

# %% Polish
# 1. Zdefiniuj coroutine function `a()`
#    która po uruchomieniu powinna
#       - wypisać: 'a: before'
#       - czekać: 1.0 sekundy
#       - wypisać: 'a: after'
# 2. Zdefiniuj coroutine function `b()`
#    która po uruchomieniu powinna
#       - wypisać: 'b: before'
#       - czekać: 0.5 sekundy
#       - wypisać: 'b: after'
# 3. Zdefiniuj coroutine function `c()`
#    która po uruchomieniu powinna
#       - wypisać: 'c: before'
#       - czekać: 1.5 sekundy
#       - wypisać: 'c: after'
# 4. Użyj funkcji `asyncio.sleep()`
# 5. Użyj słowa kluczowego `await` przed `sleep`
# 6. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `asyncio.sleep()`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 10), \
'Python 3.10+ required'

>>> from inspect import iscoroutine, iscoroutinefunction
>>> import asyncio

>>> assert iscoroutinefunction(a)
>>> assert iscoroutinefunction(b)
>>> assert iscoroutinefunction(c)
>>> assert iscoroutine(a())
>>> assert iscoroutine(b())
>>> assert iscoroutine(c())

>>> async def main():
...     return await asyncio.gather(a(), b(), c())
>>>
>>> result = asyncio.run(main())
a: before
b: before
c: before
b: after
a: after
c: after
"""

import asyncio

# Define coroutine function `a()`
# which after running should
# - print: 'a: before'
# - sleep: 1.0 second
# - print: 'a: after'
# Use function `asyncio.sleep()`
# Use `await` keyword in front of `sleep`
# type: Coroutine
async def a():
    print('a: before')
    ...
    print('a: after')

# Define coroutine function `b()`
# which after running should
# - print: 'b: before'
# - sleep: 0.5 second
# - print: 'b: after'
# Use function `asyncio.sleep()`
# Use `await` keyword in front of `sleep`
# type: Coroutine
async def b():
    print('b: before')
    ...
    print('b: after')

# Define coroutine function `c()`
# which after running should
# - print: 'c: before'
# - sleep: 1.5 second
# - print: 'c: after'
# Use function `asyncio.sleep()`
# Use `await` keyword in front of `sleep`
# type: Coroutine
async def c():
    print('c: before')
    ...
    print('c: after')