Django
async task
asynchronous programming
Django tasks
Python Django

how to do async task in django?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

In Django, "async task" can mean two different things. It can mean an async def view that handles I/O without blocking the request thread, or it can mean a background job that runs outside the request-response cycle. Those are not the same tool, and choosing the wrong one creates a lot of confusion.

Async Views Are Not Background Jobs

Django supports asynchronous views under ASGI, which is useful when your view needs to await external I/O such as HTTP calls.

python
1import httpx
2from django.http import JsonResponse
3
4
5async def weather_view(request):
6    async with httpx.AsyncClient() as client:
7        response = await client.get("https://api.example.com/weather")
8    return JsonResponse(response.json())

This makes the request handling non-blocking for compatible I/O, but it still runs as part of the web request. If the work should continue after the response is sent, you need a background task instead.

That distinction is the first decision to make.

Django Background Tasks in Current Django

Current Django includes a Tasks framework for background work. Tasks are defined with the task decorator and then enqueued.

python
1from django.core.mail import send_mail
2from django.tasks import task
3
4
5@task
6def email_users(emails, subject, message):
7    return send_mail(
8        subject=subject,
9        message=message,
10        from_email=None,
11        recipient_list=emails,
12    )

You enqueue the task from view code or other application logic.

python
1from django.http import JsonResponse
2
3
4def invite_view(request):
5    email_users.enqueue(
6        ["[email protected]"],
7        "Welcome",
8        "Thanks for signing up.",
9    )
10    return JsonResponse({"status": "queued"})

This is the correct shape for work that should happen outside the request path.

Configure a Task Backend

Django's task system provides the API and plumbing, but actual execution depends on a configured backend. The built-in immediate backend runs tasks right away and is useful for development, not for real background processing.

python
1TASKS = {
2    "default": {
3        "BACKEND": "django.tasks.backends.immediate.ImmediateBackend",
4    }
5}

That setting is good for local development because it lets you add task logic before real background infrastructure exists. For true background execution, you need an external task backend that actually queues and runs jobs outside the request thread.

So the practical rule is:

  • use ImmediateBackend for development or tests
  • use a real task backend for production background execution

Working With Sync Code in Async Views

Even in async views, not all Django internals are fully async-native. If you need to call synchronous code from async context, wrap it using sync_to_async.

python
1from asgiref.sync import sync_to_async
2from django.http import JsonResponse
3from myapp.models import Order
4
5
6async def order_count_view(request):
7    count = await sync_to_async(Order.objects.count, thread_sensitive=True)()
8    return JsonResponse({"count": count})

This matters because trying to use sync-only parts of Django directly inside async code can trigger SynchronousOnlyOperation errors.

Choosing the Right Pattern

Use an async view when the request itself needs concurrent I/O and the response still depends on the result. Use a background task when the work can outlive the request, such as sending mail, generating reports, or resizing images.

A helpful way to think about it is:

  • async view: the user is still waiting for the result
  • background task: the user is not waiting for the full work to finish

If you blur those two cases, you usually end up with slow requests or complicated code that still blocks in the wrong place.

Version and Ecosystem Considerations

If you are on current Django, use its built-in task API as the first conceptual model for background work. If you are maintaining an older Django project or already rely on an established queue system, you may still see Celery or RQ in existing codebases.

The important design principle stays the same: background jobs belong in a queue-backed worker process, not inside the request thread.

Common Pitfalls

The most common mistake is calling a long-running function directly from a view and assuming that makes it "async" just because the function is fast on a development machine.

Another issue is using an async view when the real requirement is a background job. Async views help with non-blocking request handling, but they do not detach work from the request lifecycle.

Developers also often forget that current Django still has synchronous areas. Direct ORM use in async contexts may require sync_to_async.

Finally, the built-in immediate task backend is useful for development, but it does not provide real background execution. Do not mistake it for a production queue.

Summary

  • In Django, async views and background tasks solve different problems.
  • Use async def views for non-blocking request-time I/O.
  • Use Django tasks for work that should be queued outside the request-response path.
  • Configure a real task backend for production background execution.
  • Wrap sync-only Django operations with sync_to_async when calling them from async code.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.