Is it safe to use asyncio on the server?

Hi @mark.breuss,

The official answer here is yes, it is safe and supported to use asyncio in server modules. Multithreading isn’t safe, but async runs concurrent tasks that spend a long time waiting in the same thread – and if it’s all in the same thread, Anvil’s thread-local machinery doesn’t mind at all!

You can therefore launch async functions in both Server Functions and Background Tasks, with asyncio.run.

For example, the following code works as expected:

import asyncio

async def hello():
    print('Hello ...')
    await asyncio.sleep(1)
    print('... World!')
    return 42

@anvil.server.callable
def test_run():
    return asyncio.run(hello())

This will have the desired outcome: When you call this, the console will say Hello..., then wait a second, then ...World, and return 42.

So here, the @anvil.server.callable is an ordinary function that runs in a single thread, launching a self-contained single-threaded async runner with an async function in it, and returning its result.

This enables you to do patterns like this, if you have many tasks that wait for (eg) IO in an async-y way:

async def multi_hello():
    calls = [hello() for i in range(10)]
    return await asyncio.gather(*calls)

@anvil.server.callable
def test_run_multi():
    return asyncio.run(multi_hello())

This function will also work as expected - it will print ten Hello...s, and a second later ten ...Worlds, then return [42, 42, 42, 42, ...].


As for autocomplete, this is likely(?) a bug/oversight in our parsing and completion. The Python 3.7 grammar we currently use can deal with async just fine, so it should just be a bugfix, but I haven’t personally touched that code in a couple of years so can’t speak authoritatively.

For now, however, using async will break autocompletion in the module where it’s used, which I’m aware is frustrating. I’d suggest keeping your usage of async “small”, and confined to certain modules that need it, so autocomplete remains working elsewhere.

7 Likes