17 Matching Annotations
  1. Dec 2023
    1. There are common errors experienced by beginners when getting started with asyncio in Python.

      They are:

      1. Trying to run coroutines by calling them.
      2. Not letting coroutines run in the event loop.
      3. Using the asyncio low-level API.
      4. Exiting the main coroutine too early.
      5. Assuming race conditions and deadlocks are impossible.
    1. The difference between asyncio.sleep() and time.sleep() is that asyncio.sleep() is non-blocking.
    2. The calls don't actually get made until we schedule them with await asyncio.gather(*tasks). This runs all of the tasks in our list and waits for them to finish before continuing with the rest of our program.
    3. programming with asyncio pretty much enforces* using some sort of "main" function.

      This is because you need to use the "async" keyword in order to use the "await" syntax, and the "await" syntax is the only way to actually run other async functions.`

    4. When should you use multiprocessing vs asyncio or threading?
      1. Use multiprocessing when you need to do many heavy calculations and you can split them up.
      2. Use asyncio or threading when you're performing I/O operations -- communicating with external resources or reading/writing from/to files.
      3. Multiprocessing and asyncio can be used together, but a good rule of thumb is to fork a process before you thread/use asyncio instead of the other way around -- threads are relatively cheap compared to processes.
    5. Is it possible to combine asyncio with multiprocessing?

      We can do that too.

    6. When should you use threading, and when should you use asyncio?

      When you're writing new code, use asyncio. If you need to interface with older libraries or those that don't support asyncio, you might be better off with threading.

    7. Why is the asyncio method always a bit faster than the threading method?

      This is because when we use the "await" syntax, we essentially tell our program "hold on, I'll be right back," but our program keeps track of how long it takes us to finish what we're doing. Once we're done, our program will know, and will pick back up as soon as it's able. Threading in Python allows asynchronicity, but our program could theoretically skip around different threads that may not yet be ready, wasting time if there are threads ready to continue running.

    1. There is another way to declare a route with FastAPI

      Using the asyncio:

      ``` import asyncio

      from fastapi import FastAPI

      app = FastAPI()

      @app.get("/asyncwait") async def asyncwait(): duration = 0.05 await asyncio.sleep(duration) return {"duration": duration} ```

    1. Use Python asyncio.as_completed

      There will be moments when you don't have to await for every single task to be processed right away.

      We do this by using asyncio.as_completed which returns a generator with completed coroutines.

    2. When to use Python Async

      Async only makes sense if you're doing IO.

      There's ZERO benefit in using async to stuff like this that is CPU-bound:

      ``` import asyncio

      async def sum_two_numbers_async(n1: int, n2: int) -> int: return n1 + n2

      async def main(): await sum_two_numbers_async(2, 2) await sum_two_numbers_async(4, 4)

      asyncio.run(main()) ```

      Your code might even get slower by doing that due to the Event Loop.

      That's because Python async only optimizes IDLE time!

    3. If you want 2 or more functions to run concurrently, you need asyncio.create_task.

      Creating a task triggers the async operation, and it needs to be awaited at some point.

      For example:

      task = create_task(my_async_function('arg1')) result = await task

      As we're creating many tasks, we need asyncio.gather which awaits all tasks to be done.

    1. So what's the deal with asyncio, twisted, gevent, trio and all that stuff?


      asyncio is the modern module for asynchronous network programming provided with the python stdlib since 3.4. In other words, it's the default stuff at your disposal if you want to code something without waiting on the network.

      asyncio replaces the old deprecated asyncore module. It is quite low level, so while you can manually code most network-related things with it, you are still at the level of TCP or UDP. If you want higher-level protocols, like FTP, HTTP or SSH, you have to either code it yourself, or install a third party library or module.

      Because asyncio is the default solution, it has a the biggest ecosystem of 3rd party libs, and pretty much everything async strives to be compatible with it directly, or through compatibility layers like anyio.


      20 years ago, there was no asyncio, there was no async/await, nodejs didn't exist and Python 3 was half a decade away. Yet, it was the .com bubble, everything needed to be connected now. And so was born twisted, the grandfather of all the asynchronous frameworks we have today. Twisted ecosystem grew to include everything, from mail to ssh.

      To this day, twisted is still a robust and versatile tool. But you do pay the price of its age. It doesn't follow PEP8 very well, and the design lean on the heavy size.


      Tornado was developed after Twisted, by FriendFeed, at this weird 2005-2015 web dev period where everything needed to be social web scale. It was like Twisted, but tooted to be faster, and was higher level. Out of the box, the HTTP story is way nicer.

      Today, you are unlikely to use Tornado unless you work at Facebook or contribute to jupyter. After all, if you want to make async web things, the default tool is FastAPI in 2023.


      Gevent came about in 2009, the same year as Tornado, but with a fundamentally different design. Instead of attempting to provide an asychronous API, it decided to do black magic. When you use gevent, you call from gevent import monkey; monkey.patch_all() and it changes the underlying mechanism of Python networking, making everything non-blocking.

    2. asyncio, twisted, tornado and gevent have one trick up their sleeve: they can send a message to the network, and while waiting for the response, wake up another part of the program to do some other work. And they can do that with many messages in a row. While waiting for the network, they can let other parts of the program use the CPU core.

      Note that they only can speed up waiting on the network. They will not make two calculations at the same time (can't use several CPU cores like with multiprocessing) and you can't speed up waiting on other types of I/O (like when you use threads to not block on user input or disk writes).

      All in all, they are good for writing things like bots (web crawler, chat bots, network sniffers, etc.) and servers (web servers, proxies, ...). For maximum benefits, it's possible to use them inside other concurrency tools, such as multiprocessing or multithreading. You can perfectly have 4 processes, each of them containing 4 threads (so 16 threads in total), and each thread with their own asyncio loop running.

  2. Apr 2018