[Fixed]-Django — async_to_sync vs asyncio.run



  1. They have different purposes. async_to_sync turns an awaitable into a synchronous callable, and asyncio.run executes a coroutine and return the result.

  2. According to documentation, a callable from async_to_sync works in a subthread.

  3. async_to_sync does not create an event loop per-thread in case when you’re inside synchronous code which is produced by sync_to_async and running inside asynchronous code. It reuses a loop of asynchronous code. Let’s take an example:

import asyncio
from asgiref.sync import async_to_sync, sync_to_async

async def running(n):
    return [await sync_to_async(sync)(i) for i in range(n)]

def sync(n):
    # it will create a new loop for every call
    return asyncio.run(from_sync(n))

async def from_sync(n):
    return n

print("Result:", asyncio.run(running(3)))

This one will run 4 loops: 1 to call running and 3 to call from_sync.

If we use async_to_sync instead of asyncio.run inside sync we will reduce the number of loops to 1 to call running.

To see it you can wrap new_event_loop function:

def print_deco(fn, msg):
    def inner():
        res = fn()
        print(msg, res)
        return res
    return inner
p = asyncio.get_event_loop_policy()
p.new_event_loop = print_deco(p.new_event_loop, "NEW EVENT LOOP:")

You can find a detailed explanation in this post.

Leave a comment