We’ve learned the vast majority of what asyncio has to offer. Using the modules of asyncio covered in previous chapters, you should be able to complete almost any task you need. That said, there are still a few lesser-known techniques that you may need to use, especially if you’re designing your own asyncio APIs.
In this chapter, we’ll learn a smorgasbord of more advanced techniques available in asyncio. We’ll learn how to design APIs that can handle both coroutines and regular Python functions, how to force iterations of the event loop, and how to pass state between tasks without ever passing arguments. We’ll also dig into more details on how exactly asyncio uses generators to fully understand what is happening under the hood. We’ll do this by implementing our own custom awaitables and using them to build our own simple implementation of an event loop that can run multiple coroutines concurrently.