KAHIBARO
Discord Login Register

Synchronous vs Asynchronous Tasks

Understanding Task Execution Styles

When you build backend systems, you constantly make a choice: should this work run now and block the user until it is done, or can it run later in the background?

That choice is the heart of synchronous vs asynchronous tasks.

In this chapter you will:

We will not go deep into async programming syntax or tools here, those topics appear in other chapters. Here we focus on concepts and architecture.

Key idea:
A synchronous task blocks and makes the caller wait until it is finished.
An asynchronous task is started, but the caller does not wait for it to finish.


What Is a Synchronous Task?

A synchronous task is work that runs from start to finish in one continuous flow, and the caller waits for it to complete.

In a web backend, this usually means:

Simple synchronous flow

Imagine a user signs up:

  1. User submits the signup form.
  2. Server:
    • Validates data.
    • Saves the user in the database.
    • Sends a welcome email.
  3. Only after the email is sent, the server returns 201 Created.

If sending the email takes 2 seconds, then the whole request might take 2.2 seconds. The user sees a spinning loader for 2.2 seconds.

Synchronous example in pseudocode

python
def register_user(request):
    user_data = request.body
    validate(user_data)
    user = save_user_to_db(user_data)
    send_welcome_email(user.email)  # This may take a long time
    return {"status": "ok", "user_id": user.id}

Here, the HTTP response waits for the email to finish sending.

Pros of synchronous tasks

Cons of synchronous tasks

Synchronous makes sense when:

What Is an Asynchronous Task?

An asynchronous task is work that can:

In backend systems, "asynchronous" usually combines two ideas:

  1. Non-blocking I/O or async programming (technical side).
  2. Background task execution using a queue and workers (architectural side).

Here we focus on the second: tasks that run in the background, outside the immediate HTTP request.

Asynchronous flow for the same signup

Same user signup, but handled asynchronously:

  1. User submits the signup form.
  2. Server:
    • Validates data.
    • Saves the user in the database.
    • Queues a background job to send the welcome email.
    • Returns 201 Created immediately.
  3. A worker process later reads the queued job and sends the email.

So the user sees a fast response, 0.2 seconds, instead of 2.2 seconds.

Asynchronous example in pseudocode

python
def register_user(request):
    user_data = request.body
    validate(user_data)
    user = save_user_to_db(user_data)
    # Do NOT send email here.
    # Instead, enqueue a background job.
    enqueue_job("send_welcome_email", {"email": user.email})
    return {"status": "ok", "user_id": user.id}
def worker_loop():
    while True:
        job = get_next_job()
        if job.name == "send_welcome_email":
            send_welcome_email(job.payload["email"])

The HTTP request finishes quickly, and the email is handled later.

Pros of asynchronous tasks

Cons of asynchronous tasks

Async makes sense when:

Comparing Synchronous vs Asynchronous

Think of a restaurant:

Side‑by‑side comparison


AspectSynchronousAsynchronous
Response time to userSlower for heavy tasksFast, heavy work runs in background
Implementation complexitySimpleMore complex (queues, workers, monitoring)
Resource usageRequest holds resources until doneWork can be spread across many workers
Error handlingImmediate, simple try/exceptRequires retries, dead-letter queues, status tracking
Suitable forShort tasks, must-know-now resultsLong tasks, bulk work, unreliable external services
ExampleLogin, balance check, simple CRUD writeSend emails, process images, monthly reports, webhooks

Impact on User Experience

Backend decisions directly affect what users feel.

Synchronous UX example

A photo-sharing app uploads a photo and generates several thumbnails.

Synchronous approach:

  1. User uploads photo.
  2. Server:
    • Stores original file.
    • Generates 5 thumbnail sizes.
  3. Only when all thumbnails are ready, response returns.

Users see a long spinner for several seconds before the page loads.

Asynchronous UX example

Same feature, asynchronous:

  1. User uploads photo.
  2. Server:
    • Stores original file.
    • Queues task to generate thumbnails.
    • Returns response immediately.
  3. Client:
    • Shows the original or a "processing" state.
    • Polls or uses WebSockets to know when thumbnails are ready.

Users see almost instant feedback and can continue browsing.

Important rule:
Use asynchronous background tasks for operations that are:

  • Slow or resource intensive,
  • Not required for the immediate response, and
  • Safe to finish a bit later.

Examples of such operations:

CPU‑Bound vs I/O‑Bound Work

Not all tasks are slow for the same reason. Understanding why something is slow helps you decide how to run it.

I/O‑bound tasks

These tasks spend most of their time waiting for something external:

I/O‑bound tasks are good candidates for:

CPU‑bound tasks

These tasks spend most of their time calculating:

CPU‑bound tasks are good candidates for:

Guideline:

  • If a task is I/O‑bound and long, prefer async or background jobs.
  • If a task is CPU‑bound and heavy, prefer background jobs, often with separate worker processes.

Synchronous Tasks Inside a Single Request

You will still use synchronous logic all the time, even in modern async frameworks.

Typical synchronous operations inside a single HTTP request:

Example: fetch user profile

python
def get_profile(request, user_id):
    user = db.get_user(user_id)           # small DB query
    if user is None:
        return {"error": "not found"}, 404
    return {"id": user.id, "name": user.name}

This entire flow can safely be synchronous inside one request. No need for background tasks.


Asynchronous Tasks with Queues and Workers

In backend systems, asynchronous tasks often involve these components:

  1. Producer: The part of your app that decides a background job is needed.
  2. Queue: Where jobs are stored temporarily.
  3. Worker: A separate process that reads jobs and executes them.

Example flow: generating a PDF invoice

  1. Client: POST /invoices with order details.
  2. API server:
    • Stores invoice record in database.
    • Queues a job: "generate_invoice_pdf", {"invoice_id": 123}.
    • Returns 202 Accepted or 201 Created.
  3. Worker:
    • Reads job from queue.
    • Generates PDF, stores it in object storage.
    • Updates invoice record with PDF URL.
  4. Client:
    • Polls GET /invoices/123 or uses notifications to see when PDF is ready.

This is a very common pattern in real systems.


Choosing Between Synchronous and Asynchronous

You choose per operation, not per project. The same application will use both.

Ask these questions for each piece of work:

  1. Does the user need the result right now to continue?
    • Yes: probably synchronous.
    • No: candidate for async job.
  2. How long does it take?
    • Under ~100 ms: usually fine in the main request.
    • Hundreds of ms to seconds: consider async, especially under load.
  3. What happens if it fails?
    • Must succeed immediately: maybe synchronous with retries.
    • Can be retried later: good for async processing with retry strategies.
  4. How heavy is it on resources?
    • CPU heavy or uses a lot of memory: background worker, controlled concurrency.
    • Light operations: keep synchronous.

Common examples


OperationTypical ChoiceReason
User loginSynchronousMust know result immediately
Fetch product listSynchronousMust display now
Send "welcome" emailAsynchronousCan be delayed, may be slow
Generate weekly reportAsynchronousHeavy, can be scheduled at night
Resize uploaded imagesAsynchronousCPU heavy, can finish after upload
Payment authorizationSynchronousUser must know if payment went through
Rebuilding search indexAsynchronousVery heavy, not tied to a single request

Simple Patterns You Will Use Often

Here are three very simple architectural patterns that use both synchronous and asynchronous tasks.

1. Fire‑and‑forget background job

Use when you do not need to report back to the user about the job status.

Example: log extra analytics data.

2. Background job with polling

Use when the user eventually needs the result.

3. Background job with notification

Use when you can push the result to the client.

Summary

In the rest of the Background Processing section, you will learn about background jobs, message queues, and workers, which are the tools that bring asynchronous tasks to life in real backend systems.

Views: 10

Comments

Please login to add a comment.

Don't have an account? Register now!