Easy Call async functions in sequential code [Call coroutines in django views]

If you ever tried to call coroutines in Django views, you probably know that it returns a coroutine object. It is related to the fact that Django views are sequential code. When you call async functions in sequential code, the return value is a coroutine object.

Unfortunately, you can not simply call the async function with the await construct. If you try this, you get a SyntaxError: ‘await’ outside async function. In this tutorial, we learn how to call async functions in sequential code and apply this technique to call coroutines in Django views.

Call coroutines in django views

Let see the errors you get when you call async functions in sequential code. The sequential code we demonstrate the problem is Django views. Suppose you have coroutines declared with async syntax, and you want to call coroutines in Django views; that is, you want to call coroutines in sequential code.

For the sake of simplicity, suppose the coroutine with async syntax return a string. In the Django view, you want to call this coroutine, find the generated string and respond with text file whose content is this value.

Call async functions in sequential code – Always return coroutine object

It seems straightforward task. You naively try to write the following code:

data.py – The module with coroutine declared with async syntax
import asyncio

async def msg():
    await asyncio.sleep(2)
    return 'hello'
urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('hello', views.hello, name='hello')
]
views.py
from . import data

def hello(request):
    ss = data.hello()
    return HttpResponse(ss,content_type='text/plain')

However when you test the URL you get the following text file:

Text File at /hello
<coroutine object msg at 0x7f8504696d48>

This example shows that you will always get a coroutine object when you call async functions in sequential code. It is nice, but it not what you want to do. You want to return the value generated from the async function.

‘await’ outside async function

Fortunately, You remember that you can use the await statement to get the generated from the async function and decide to modify the code to use await construct.

views.py
from . import data

def hello(request):
    ss = await data.hello()
    return HttpResponse(ss,content_type='text/plain')

When test the code by accessing the hello url, you get:

SyntaxError: ‘await’ outside async function
$ ./manage.py runserver 0:8000
Performing system checks...

Unhandled exception in thread ...
ss = await data.msg()
         ^

SyntaxError: 'await' outside async function

It does not solve the problem. Now, the code raises an exception. You get SyntaxError exception with a clear message 'await' outside async function.

async functions always return coroutine object

Well, You may think it straightforward to fix this issue. You decide to modify the code and convert the view function to coroutine with the async construct.

views.py
from . import data

async def hello(request):
    ss = await data.hello()
    return HttpResponse(ss,content_type='text/plain')

When test the code by accessing the hello url, you get, An exception is raised when accessing the url:

exception at /hello : exception ‘coroutine’ object has no attribute ‘get’
 AttributeError at /hello
 'coroutine' object has no attribute 'get'

Oops! As we see earlier, a function with async syntax will always return coroutine object. However, sometimes your API require that you provide a regular function and not a coroutine.

In this example, Django (at least, Django 2.15) expect you return a HttpResponse object. However, when it try to call the method get method of the HttpResponse object, It can not find this methode and raises this exception.

How to Call async functions in sequential code?

Let’s see how to call async functions in sequential code. we will use the asyncio module

First, we create a new event loop with asyncio.new_event_loop

loop = asyncio.new_event_loop()

The loop will be responsible for executing coroutine or the async functions we provide it. We can tell the loop to wait until the coroutine is completed and return the generated value using run_until_complete method.

ss = loop.run_until_complete( data.msg() )

We need to close the loop when we do not need the loop anymore, so the loop releases the unused resources.

 loop.close()

Putting it all together, we get the following code:

Call async functions in sequential code

from . import data

def hello(request):
    loop = asyncio.new_event_loop()
    ss = loop.run_until_complete( data.msg() )
    loop.close()

    return HttpResponse(ss,content_type='text/plain')

Now, When test the code by accessing the hello url, you get, An exception is raised when accessing the URL, we get the expected contents.

Text File at /hello
hello

We see the problems you get when you call async functions in sequential code and how to overcome the issue. What do you think? Do you have a better solution?

Leave a Reply

Your email address will not be published. Required fields are marked *