# How ComfyUI's Async/Await Server Architecture Handles Prompts: A Deep Dive

> Discover how ComfyUI's async await server architecture processes prompts via aiohttp, enabling validation, queuing, and execution without blocking, for concurrent requests and real-time WebSocket updates.

- Repository: [Comfy Org/ComfyUI](https://github.com/Comfy-Org/ComfyUI)
- Tags: deep-dive
- Published: 2026-02-26

---

**ComfyUI processes prompts through an asynchronous pipeline built on aiohttp that validates, enqueues, and executes generation workflows without blocking the HTTP server, enabling concurrent request handling and real-time WebSocket progress updates.**

ComfyUI leverages a fully asynchronous HTTP and WebSocket server built on **aiohttp** to manage Stable Diffusion generation workflows. The async/await server architecture in the Comfy-Org/ComfyUI repository handles prompts through three distinct stages: reception and validation, enqueueing, and asynchronous execution. This design allows the server to accept multiple concurrent requests while streaming fine-grained progress updates to connected clients.

## Async Prompt Reception and Validation

The prompt lifecycle begins when a client POSTs JSON to the `/prompt` endpoint. In [`server.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/server.py) (lines 72-79), the `PromptServer` instance receives the payload and optionally patches it through the `NodeReplaceManager`.

Validation occurs immediately via `execution.validate_prompt` (defined in [`execution.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/execution.py), lines 14-30). This async function traverses the workflow graph to confirm each node class exists, verifies that at least one **output node** is present, and validates input connections. Invalid prompts return immediately as JSON responses, preventing them from ever entering the execution queue.

## The PromptQueue System

Once validated, prompts enter the **PromptQueue** class defined in [`execution.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/execution.py) (lines 37-55). The server wraps each prompt in a tuple structure:

```python
(number, prompt_id, prompt, extra_data, outputs_to_execute, sensitive)

```

This tuple is pushed onto a thread-safe **heapq** protected by an `RLock` and `Condition` variable via `self.prompt_queue.put()` (as seen in [`server.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/server.py), lines 90-92). The heap structure enables priority ordering while maintaining thread safety across async boundaries. The server immediately returns the assigned `prompt_id` and queue `number` to the client, allowing the HTTP connection to close while processing continues asynchronously.

## Asynchronous Execution Engine

A dedicated **worker coroutine** running in the main event loop continuously polls the queue. In [`main.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/main.py) (lines 7-13), the `run()` coroutine enters a loop that calls `q.get(timeout=...)` on the `PromptQueue`.

When a prompt is dequeued, the worker extracts the data and invokes the execution engine via `e.execute()` (lines 50-62 in [`main.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/main.py)). The `execution.execute` function (defined in [`execution.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/execution.py), line 413) is fully async and:

- Resolves node inputs through `_async_map_node_over_list`
- Awaits each node's `FUNCTION` or async `execute` method when required
- Respects interruption signals via `comfy.model_management.throw_exception_if_processing_interrupted`

After execution completes, the worker triggers garbage collection and model unloading logic (lines 84-99 in [`main.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/main.py)) to free GPU/CPU memory before processing the next item.

## Real-Time WebSocket Communication

During execution, the engine streams progress updates through the **WebSocket publish loop**. The `PromptServer.publish_loop` (lines 81-84 in [`server.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/server.py)) broadcasts incremental events including `executed`, `status`, and `preview_image` messages to connected clients.

This async communication channel operates independently of the HTTP request lifecycle, enabling live progress bars and preview images even for long-running generation tasks. Binary image data transfers utilize protocols defined in [`protocol.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/protocol.py) for efficient streaming.

## Practical Implementation Examples

### Submitting a Prompt via HTTP

Clients submit workflows through the REST API endpoint handled in [`server.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/server.py):

```python
import requests, uuid

prompt = {
    "prompt": {
        "3": {"class_type": "LoadImage", "inputs": {"url": "https://example.com/img.png"}},
        "4": {"class_type": "CLIPTextEncode", "inputs": {"text": "a sunset over mountains"}},
        "5": {"class_type": "KSampler", "inputs": {"seed": 42, "steps": 20, "cfg": 7.0}},
        "6": {"class_type": "SaveImage", "inputs": {"filename_prefix": "out_"}, "output": True}
    },
    "prompt_id": str(uuid.uuid4())
}
resp = requests.post("http://127.0.0.1:8188/prompt", json=prompt)
print(resp.json())  # Returns: {"prompt_id": "...", "number": 0, "node_errors": {}}

```

### Listening for Async Events via WebSocket

Real-time updates flow through the WebSocket handler implemented in `PromptServer.websocket_handler` ([`server.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/server.py), lines 50-71):

```python
import asyncio, websockets, json

async def listen():
    async with websockets.connect("ws://127.0.0.1:8188/ws?clientId=myclient") as ws:
        init = json.loads(await ws.recv())
        print("Queue status:", init)
        
        while True:
            msg = json.loads(await ws.recv())
            typ, data = msg["type"], msg["data"]
            if typ == "executed":
                print(f"Node {data['node']} finished")
            elif typ == "progress":
                print(f"Progress: {data['value']}/{data['max']}")

asyncio.run(listen())

```

### Direct Async API Access

For unit testing or embedded deployments, bypass HTTP entirely using the internal async API:

```python
import asyncio, uuid
from server import PromptServer
from execution import PromptQueue, validate_prompt, execute

async def run_prompt():
    loop = asyncio.get_event_loop()
    srv = PromptServer(loop)
    queue = PromptQueue(srv)
    
    prompt = {...}  # Workflow JSON

    prompt_id = str(uuid.uuid4())
    
    valid, _, outputs, _ = await validate_prompt(prompt_id, prompt, None)
    if not valid:
        raise RuntimeError("Invalid prompt")
    
    queue.put((0, prompt_id, prompt, {}, outputs, {}))
    item, _ = queue.get()
    await execute(srv, item[2], {}, {}, item[4], set(), set(), set(), {})

asyncio.run(run_prompt())

```

## Summary

- **ComfyUI** implements a non-blocking prompt pipeline using **aiohttp** async route handlers in [`server.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/server.py) to accept concurrent HTTP requests without thread blocking.
- The **PromptQueue** in [`execution.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/execution.py) utilizes a thread-safe heapq with RLock and Condition variables to manage workflow ordering and synchronization.
- **Async validation** occurs immediately upon reception via `execution.validate_prompt`, preventing invalid workflows from consuming resources.
- A dedicated **worker coroutine** in [`main.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/main.py) pulls items from the queue and executes them via `execution.execute`, supporting both sync and async node functions through `await` patterns.
- Real-time progress flows through **WebSocket publish loops**, streaming `executed` and `preview_image` events independently of the request lifecycle.

## Frequently Asked Questions

### How does ComfyUI prevent invalid prompts from entering the execution queue?

The `execution.validate_prompt` async function (lines 14-30 in [`execution.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/execution.py)) validates the workflow graph structure, confirms node class existence, and checks for required output nodes before enqueueing. Invalid prompts receive immediate JSON error responses, ensuring only valid workflows reach the **PromptQueue**.

### Can the prompt queue handle priority scheduling?

Yes. The **PromptQueue** implementation in [`execution.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/execution.py) (lines 37-55) uses a **heapq** data structure, allowing prompts to be inserted with priority values. The heap ordering ensures higher-priority workflows execute first while maintaining thread safety through RLock and Condition primitives.

### What enables real-time progress updates during generation?

The **WebSocket publish loop** (`PromptServer.publish_loop` in [`server.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/server.py), lines 81-84) broadcasts incremental execution events asynchronously. As the engine processes each node via `execution.execute`, it dispatches `progress`, `executed`, and `preview_image` messages to connected clients without blocking the main computation thread.

### How does the architecture handle GPU memory cleanup between prompts?

After each execution cycle, the worker coroutine in [`main.py`](https://github.com/Comfy-Org/ComfyUI/blob/main/main.py) (lines 84-99) triggers explicit garbage collection and model unloading logic. This ensures GPU/CPU resources are released before the next `q.get()` call retrieves subsequent prompts from the **PromptQueue**, preventing memory leaks during long-running server operations.