11

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.

Raphael Todo
  • 359
  • 1
  • 2
  • 16

4 Answers4

5

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.

pgjones
  • 6,044
  • 1
  • 14
  • 12
  • Yeah it is normally a post, I misused a get in the working snippet. I'm a bit freaked out to change all of my framework (even if it seems to be mostly the same syntax) just for only one function, I'm gonna try eventlet! – Raphael Todo Jun 22 '18 at 08:56
  • isn't it bad to have async code executed inside flask process? – Mojimi Jan 25 '19 at 14:40
5

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()
luc.chante
  • 418
  • 7
  • 18
  • 1
    This will disable all advantages of coroutines. If say there are two requests calling the index page, flask will handle the 2nd request until the 1st request is completed, while correctly using coroutines, we can handle the 2nd request concurrently with the 1st request. – Kipsora Lawrence Jun 26 '20 at 19:20
0

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
Pitto
  • 8,229
  • 3
  • 42
  • 51
  • My blocking function get some ressource with request (so yeah, could use grequest here) and then save it in database, if I use grequest all of the saving in db will be async too? – Raphael Todo Jun 20 '18 at 07:41
  • The problem here is that my client will have to wait the response of the slow api to have a response. – Raphael Todo Jun 20 '18 at 07:44
  • I suppose there aren't many solutions for that... If the only possibility to move forward is getting the answer from a slow API... The API is the culprit. You can put sugar on it (lovely loading image, similar UX/UI magic) but... In the end you'll wait, right? I'd try grequests, if you do not want to use other solutions that are more complex like Celery. Test is going to be quick and you'll understand if this path can work fine for you or not. – Pitto Jun 20 '18 at 07:46
0

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

Jibin Mathew
  • 4,816
  • 4
  • 40
  • 68