I have been tinkering around Flask and FastAPI to see how it acts as a server.
One of the main things that I would like to know is how Flask and FastAPI deal with multiple requests from multiple clients.
Especially when the code has efficiency issues (long database query time).
So, I tried making a simple code to understand this problem.
The code is simple, when the client access the route, the application sleeps for 10 seconds before it returns results.
It looks something like this:
FastAPI
import uvicorn
from fastapi import FastAPI
from time import sleep
app = FastAPI()
@app.get('/')
async def root():
print('Sleeping for 10')
sleep(10)
print('Awake')
return {'message': 'hello'}
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8000)
Flask
from flask import Flask
from flask_restful import Resource, Api
from time import sleep
app = Flask(__name__)
api = Api(app)
class Root(Resource):
def get(self):
print('Sleeping for 10')
sleep(10)
print('Awake')
return {'message': 'hello'}
api.add_resource(Root, '/')
if __name__ == "__main__":
app.run()
Once the applications are up, I tried accessing them at the same time through 2 different chrome clients. The below are the results:
FastAPI

Flask

As you can see, for FastAPI, the code first waits 10 seconds before processing the next request. Whereas for Flask, the code processes the next request while the 10-second sleep is still happening.
Despite doing a bit of googling, there is not really a straight answer on this topic.
If anyone has any comments that can shed some light on this, please drop them in the comments.
Your opinions are all appreciated. Thank you all very much for your time.
EDIT An update on this, I am exploring a bit more and found this concept of Process manager. For example, we can run uvicorn using a process manager (gunicorn). By adding more workers, I am able to achieve something like Flask. Still testing the limits of this, however. https://www.uvicorn.org/deployment/
Thanks to everyone who left comments! Appreciate it.
I think you are blocking an event queue in FastAPI which is asynchronous framework whereas in Flask requests are probably run each in new thread. Move all CPU bound tasks to separate processes or in your FastAPI example just sleep on event loop (do not use time.sleep here). In FastAPI run IO bound tasks asynchronously
You are using the time.sleep() function, in a async endpoint. time.sleep() is blocking and should never be used in asynchronous code. What you should be using is probably the asyncio.sleep() function:
import asyncio
import uvicorn
from fastapi import FastAPI
app = FastAPI()
@app.get('/')
async def root():
print('Sleeping for 10')
await asyncio.sleep(10)
print('Awake')
return {'message': 'hello'}
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8000)
That way, each request will take ~10 sec to complete, but you will be able to server multiple requests concurrently.
In general, async frameworks offer replacements for all blocking functions inside the standard library (sleep functions, IO functions, etc.). You are meant to use those replacements when writing async code and (optionally) await them.
Some non-blocking frameworks and libraries such as gevent, do not offer replacements. They instead monkey-patch functions in the standard library to make them non-blocking. This is not the case, as far as I know, for the newer async frameworks and libraries though, because they are meant to allow the developer to use the async-await syntax.
Blocking operations will stop your event loop running the tasks. When you are calling the sleep() function, all the tasks (requests) are waiting until it's finished, thus killing all the benefits of asynchronous code execution.
To understand why this code is wrong for comparison, we should better understand how asynchronous code works in Python and have some knowledge of GIL. Concurrency and async code are well explained in the docs of FastAPI.
@Asotos has described why your code is slow and yes, you should use coroutines for I/O operations since they block the event loop execution (sleep() is a blocking operation). It is reasonably suggested to use async functions so that the event loop is not blocked, but for now, not all libraries have async versions.
async functions and asyncio.sleepIn case you cannot use the async version of the library, you can simply define your route functions as simple def functions, not async def.
If the route function is defined as synchronous (def), FastAPI will smartly call this function in an external thread pool, and the main thread with event loop will not be blocked, and your benchmarks will be much better without using await asyncio.sleep(). Greatly explained in this section.
from time import sleep
import uvicorn
from fastapi import FastAPI
app = FastAPI()
@app.get('/')
def root():
print('Sleeping for 10')
sleep(10)
print('Awake')
return {'message': 'hello'}
if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8000)
BTW, you won't gain a lot of benefits, if operations run in the thread pool are CPU bound (e.g. heavy calculations) because of GIL. CPU-bound tasks must be run in separate processes.