By default, asyncio runs coroutines synchronously. If they contain blocking IO code, they still wait for it to return. A way around this is loop.run_in_executor(), which converts the code into threads. If a thread blocks on IO, another thread can start executing. So you don't waste time waiting for IO calls. If you use asyncio without executors, you lose those speedups. So I was wondering, why do you have to use executors explicitly. Why not enable them by default ? (In the following, I'll focus on http requests. But they really only serve as an example. I'm interested in the general principles.) After some searching I found aiohttp. It's a library that essentially offers a combination of asyncio and requests: Non blocking HTTP calls. With executors, asyncio and requests behave pretty much just like aiohttp. Is there a reason to implement a new library, do you pay a performance penalty for using executors? This question was answered: Why doesn't asyncio always use executors? Mikhail Gerasimov has explained to me that executors will spin up OS-threads and they can become expensive. So it makes sense not to have them as default behaviour. aiohttp is better than using the requests module in an executor, since it offers non-blocking code with only coroutines. Which brings me to this question. aiohttp advertises itself as : Asynchronous HTTP Client/Server for asyncio and Python. So aiohttp is based on asyncio? Why doesn't asyncio offer non-blocking code with only coroutines then? That would be the ideal default. Or did aiohttp implement this new event-loop (without OS-threads) itself ? In that case I don't understand why they advertise themselves as based on asyncio. Async/await are a language feature. Asyncio is an event-loop. And if aiohttp has its own event-loop there should be little intersection with asyncio. Actually, I would argue that such an event loop would be a much bigger feature than http requests. Continue reading...