Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django — async_to_sync vs asyncio.run

We can use both functions to run any async function synchronously:

import asyncio
from asgiref.sync import async_to_sync

asyncio.run(asyncio.sleep(1))
async_to_sync(asyncio.sleep)(1)

What is the difference? Can we always use asyncio.run instead of async_to_sync?

like image 760
Max Malysh Avatar asked Dec 27 '19 17:12

Max Malysh


People also ask

What is Async_to_sync in Django?

async_to_sync() is essentially a more powerful version of the asyncio. run() function in Python's standard library. As well as ensuring threadlocals work, it also enables the thread_sensitive mode of sync_to_async() when that wrapper is used below it.

What does Asyncio run do?

Async IO takes long waiting periods in which functions would otherwise be blocking and allows other functions to run during that downtime. (A function that blocks effectively forbids others from running from the time that it starts until the time that it returns.)

Which function is used to run Awaitables concurrently in Asyncio?

gather() method - It runs awaitable objects (objects which have await keyword) concurrently.

What is Asyncio sleep ()?

The asyncio. sleep() method suspends the execution of a coroutine. Coroutines voluntarily yield CPU leading to co-operative multitasking through the await keyword.


1 Answers

Differences

  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.

like image 159
Artemiy Rodionov Avatar answered Nov 05 '22 10:11

Artemiy Rodionov