26

I am in the process of trying to port a flask app to quart to utilise asyncio. I don't think my current approach is working, as my entire function chain is written without async in mind - consider the following:

def long_running_task(task):
    result = some_synchronous_function(task)
    return result

@app.route('/<task>', methods=['GET'])
async def do_task(task):
    ok = await long_running_task(task)
    if ok:
        return (ok.result)
    else:
        return ('Something went wrong')

If long_running_task and its whole chain of function calls are not declared as async, am I actually getting any benefit from my route being declared as async?

1
  • 3
    Nope, that shouldn't even work. You can only await awaitables — async functions etc. To use the power of async, you need to transform your I/O (DB access, etc.) to asynchronous access. Commented Feb 14, 2019 at 7:34

1 Answer 1

32

To run a blocking synchronous function from asyncio, without blocking the main event loop, you can use loop.run_in_executor() to run the blocking function in a ThreadPoolExecutor or ProcessPoolExecutor` (i.e. in its own thread or process).

From within the async function you want to call it from:

loop = asyncio.get_event_loop()

result = await loop.run_in_executor(None, long_running_task, task)

The first argument None is to tell it to use the default executor for the loop. Obviously do_task() will still have to wait for result to complete, but while it is waiting, other async tasks will be able to run in event-loop.

Sign up to request clarification or add additional context in comments.

4 Comments

Is it possible to call multiple functions to run concurrently in the same loop.run_in_executor?
@y_159 check out asyncio.gather(), you can use it to run them in parallel.
@LukeSapan asyncio.gather() is used to run tasks created by asyncio..create_task() concurrently. How would i use that here for loop.run_in_executor(). I think it starts executing the blocking function without creating a task object? My question is how to use a single loop.run_in_executor() to run multiple blocking functions concurrently.
@y_159 You can invoke loop.run_in_executor multiple times within asyncio.gather(...). Even though you're calling loop.run_in_executor multiple times, they'll all be using the default shared executor, so you aren't spawning new executors each time. Alternatively, if you don't want to use the default executor, you can create your own shared one, and pass it in as the first parameter to each call (instead of None as shown in the example above).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.