Raphael Todo
Raphael Todo

Reputation: 359

Async call method with Flask

I'm trying to call a blocking function through a Flask method but it take several second, so I was thinking I could do some async call to speed things up but it doesn't work as expected. Apparently with asyncio I can't just launch a coroutine in background and don't wait for the end of the execution, maybe I need to use thread? Or use grequest as my blocking function is using request...

Here's my code so far:

@app.route("/ressource", methods=["GET"])
def get_ressource():
    do_stuff()
    return make_response("OK",200)  

def do_stuff():
  # Some stuff
  fetch_ressource()


async def fetch_ressource():
    return await blocking_function()


def blocking_function():
  # Take 2-3 seconds
  result = request.get('path/to/remote/ressource')
  put_in_database(result)

I heard about Celeri but it seems a little overkill for only one function.

Upvotes: 11

Views: 27696

Answers (4)

luc.chante
luc.chante

Reputation: 440

It's a little bit late to answer, but I am interested in this.

I manage it by wrapping the function and calling it via asyncio.run(), but I don't know if multiple asyncio.run() calls is a good thing to do.

from functools import wraps
from flask import Flask
import asyncio

def async_action(f):
    @wraps(f)
    def wrapped(*args, **kwargs):
        return asyncio.run(f(*args, **kwargs))
    return wrapped

app = Flask(__name__)

@app.route('/')
@async_action
async def index():
    await asyncio.sleep(2)
    return 'Hello world !'

app.run()

Upvotes: 5

Jibin Mathew
Jibin Mathew

Reputation: 5122

Consider using Sanic framework, its designed with asynchronous operations in mind, and you can see the sanic version of the code here

Upvotes: 0

pgjones
pgjones

Reputation: 7039

You can do this with Quart and AIOHTTP with code that should be very familiar to the Flask code given,

@app.route("/ressource", methods=["POST"])
async def get_ressource():
    asyncio.ensure_future(blocking_function())
    return await make_response("OK", 202)  

async def blocking_function():
    async with aiohttp.ClientSession() as session:
        async with session.get('path/to/remote/ressource') as resp:
            result = await resp.text()
    await put_in_database(result)

Note: I've changed it to a POST route as it does something and I've returned a 202 response to indicate that it has triggered processing.

Should you wish to stick with Flask I recommend you use eventlet and use spawn(blocking_function) without the async or await inclusions.

Also Note I am the Quart author.

Upvotes: 6

Pitto
Pitto

Reputation: 8589

What is the blocking function doing?

Could be using grequests feasible?

import grequests

@app.route('/do', methods = ['POST'])
def do():
    result = grequests.map([grequests.get('slow api')])
    return result[0].content

Upvotes: 0

Related Questions