---
title: "Async/Await em Zig: Guia Prático Completo | Zig Brasil"
url: "https://ziglang.com.br/tutoriais/async-await-zig/"
markdown_url: "https://ziglang.com.br/tutoriais/async-await-zig.MD"
description: "Aprenda async/await em Zig: coroutines, event loops e HTTP assíncrono. Tutorial prático do modelo único de concorrência do Zig. Exemplos funcionando."
date: "2026-02-09"
author: ""
---

# Async/Await em Zig: Guia Prático Completo | Zig Brasil

Aprenda async/await em Zig: coroutines, event loops e HTTP assíncrono. Tutorial prático do modelo único de concorrência do Zig. Exemplos funcionando.


> ⚠️ **Aviso Importante**: O sistema de async/await em Zig está em evolução ativa. Enquanto os conceitos fundamentais são estáveis, algumas APIs específicas podem mudar entre versões. Este tutorial foca em padrões estabelecidos e adiciona notas onde há instabilidade.

Programação assíncrona é essencial para aplicações de alta performance que precisam lidar com milhares de conexões simultâneas. Zig oferece um modelo de async/await único — diferente de JavaScript, Rust ou Go — que dá ao desenvolvedor controle total sobre como o código assíncrono é executado.

Neste guia, você vai entender o **modelo async de Zig** desde os conceitos básicos até exemplos práticos de HTTP e manipulação de erros.

## Por Que Zig para Async?

Antes de mergulharmos na sintaxe, vamos entender o que torna o modelo async do Zig especial:

| Característica | Zig | JavaScript | Rust | Go |
|----------------|-----|------------|------|-----|
| **Runtime** | Sem runtime obrigatório | V8 engine | Tokio/async-std | Goroutines |
| **Stack** | Configurável (async/await = structs) | Call stack + microtasks | Stackless coroutines | Growable stacks |
| **Memory** | Controle explícito | GC automático | Ownership | GC |
| **Overhead** | Zero (sem runtime) | Alto (event loop) | Médio | Médio |
| **Color functions** | Não (funções são agnósticas) | Sim (async vs sync) | Sim | Não |

### O Que Torna Zig Único

1. **Sem "function coloring"**: Em Zig, funções async e sync têm a mesma assinatura
2. **Sem runtime obrigatório**: Você escolhe (ou implementa) seu event loop
3. **Zero overhead**: Async é implementado como state machines, não threads
4. **Composição elegante**: `async`/`await`/`suspend`/`resume` funcionam em harmonia

## Conceitos Fundamentais

### O Que é um "Frame"?

Em Zig, quando você marca uma função como `async`, o compilador transforma ela em uma **state machine** (máquina de estados). Essa state machine é chamada de "frame" (quadro):

```zig
const std = @import("std");

// Esta função async pode ser suspensa e retomada
fn fetchData() ![]const u8 {
    // Simula uma operação de I/O
    std.time.sleep(100 * std.time.ns_per_ms);
    return "dados carregados";
}

pub fn main() !void {
    // Inicia a função async - retorna um frame, não o resultado
    var frame = async fetchData();
    
    // await retoma a execução e retorna o resultado
    const resultado = try await frame;
    
    std.debug.print("Resultado: {s}\n", .{resultado});
}
```

### async, await, suspend, resume

Quatro palavras-chave controlam a execução assíncrona:

| Palavra-chave | Função |
|---------------|--------|
| `async` | Inicia uma função async, retorna um frame |
| `await` | Espera o frame completar, retorna o resultado |
| `suspend` | Pausa a execução, retorna controle ao chamador |
| `resume` | Retoma a execução de um frame suspenso |

```zig
const std = @import("std");

fn coroutineExample() void {
    std.debug.print("Início\n", .{});
    
    // Suspende a execução
    suspend {}
    
    std.debug.print("Após primeiro resume\n", .{});
    
    suspend {}
    
    std.debug.print("Após segundo resume\n", .{});
}

pub fn main() void {
    var frame = async coroutineExample();
    
    std.debug.print("Frame criado\n", .{});
    
    resume frame;
    std.debug.print("Primeiro resume\n", .{});
    
    resume frame;
    std.debug.print("Segundo resume\n", .{});
}
```

Output:
```
Início
Frame criado
Após primeiro resume
Primeiro resume
Após segundo resume
Segundo resume
```

## Async/Await na Prática

### Exemplo Básico: Download de Arquivos

```zig
const std = @import("std");

const DownloadError = error{
    NetworkError,
    Timeout,
};

fn downloadFile(url: []const u8) DownloadError![]const u8 {
    std.debug.print("Iniciando download de {s}...\n", .{url});
    
    // Simula latência de rede
    std.time.sleep(500 * std.time.ns_per_ms);
    
    // Simula possível erro
    if (url.len == 0) {
        return DownloadError.NetworkError;
    }
    
    return "Conteúdo do arquivo";
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    
    // Inicia múltiplos downloads concorrentemente
    const frame1 = async downloadFile("https://exemplo.com/arquivo1.txt");
    const frame2 = async downloadFile("https://exemplo.com/arquivo2.txt");
    const frame3 = async downloadFile("https://exemplo.com/arquivo3.txt");
    
    std.debug.print("Downloads iniciados!\n", .{});
    
    // Aguarda todos completarem
    const result1 = try await frame1;
    const result2 = try await frame2;
    const result3 = try await frame3;
    
    std.debug.print("Arquivo 1: {s}\n", .{result1});
    std.debug.print("Arquivo 2: {s}\n", .{result2});
    std.debug.print("Arquivo 3: {s}\n", .{result3});
}
```

### Paralelismo com async

Aqui está o ponto crucial: **async não é paralelismo**. Frames rodam em uma única thread até você explicitamente distribuí-las. Para entender as diferentes abordagens de paralelismo real com threads, veja o tutorial de [concorrência em Zig](/tutoriais/concorrencia-em-zig/):

```zig
const std = @import("std");

fn computeIntensive(n: u32) u32 {
    var sum: u32 = 0;
    var i: u32 = 0;
    while (i < n) : (i += 1) {
        sum += i;
    }
    return sum;
}

pub fn main() !void {
    // Isso NÃO roda em paralelo por padrão
    const frame1 = async computeIntensive(100_000_000);
    const frame2 = async computeIntensive(100_000_000);
    
    const result1 = await frame1;
    const result2 = await frame2;
    
    std.debug.print("Resultados: {} e {}\n", .{result1, result2});
}
```

Para verdadeiro paralelismo, combine com threads:

```zig
const std = @import("std");

fn worker(id: u32) void {
    std.debug.print("Worker {} iniciado\n", .{id});
    std.time.sleep(100 * std.time.ns_per_ms);
    std.debug.print("Worker {} completado\n", .{id});
}

pub fn main() !void {
    // Cria threads que executam async
    const t1 = try std.Thread.spawn(.{}, asyncWorker, .{1});
    const t2 = try std.Thread.spawn(.{}, asyncWorker, .{2});
    
    t1.join();
    t2.join();
}

fn asyncWorker(id: u32) void {
    var frame = async worker(id);
    await frame;
}
```

## Event Loop: O Coração do Async

Zig não fornece um event loop padrão — você escolhe ou implementa o seu. Isso dá flexibilidade total, mas também significa mais responsabilidade.

### Event Loop Simples

```zig
const std = @import("std");

const EventLoop = struct {
    frames: std.ArrayList(anyframe),
    allocator: std.mem.Allocator,
    
    pub fn init(allocator: std.mem.Allocator) EventLoop {
        return .{
            .frames = std.ArrayList(anyframe).init(allocator),
            .allocator = allocator,
        };
    }
    
    pub fn deinit(self: *EventLoop) void {
        self.frames.deinit();
    }
    
    pub fn spawn(self: *EventLoop, frame: anyframe) !void {
        try self.frames.append(frame);
    }
    
    pub fn run(self: *EventLoop) void {
        // Simplificação: em produção, usar seleção I/O (epoll, kqueue, IOCP)
        for (self.frames.items) |frame| {
            resume frame;
        }
        self.frames.clearRetainingCapacity();
    }
};

fn task(id: u32) void {
    std.debug.print("Task {} executando\n", .{id});
    suspend {}
    std.debug.print("Task {} completada\n", .{id});
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    
    var loop = EventLoop.init(allocator);
    defer loop.deinit();
    
    // Agenda tarefas
    try loop.spawn(async task(1));
    try loop.spawn(async task(2));
    try loop.spawn(async task(3));
    
    // Executa o loop
    loop.run();
}
```

> ⚠️ **Nota**: Em código de produção, use bibliotecas como `libxev` ou implementações do `std.event` (quando estável).

## HTTP Client Assíncrono

Vamos criar um exemplo prático de HTTP client não-bloqueante:

```zig
const std = @import("std");

const HttpError = error{
    ConnectionFailed,
    Timeout,
    InvalidResponse,
};

const HttpResponse = struct {
    status: u16,
    body: []const u8,
    
    pub fn deinit(self: HttpResponse, allocator: std.mem.Allocator) void {
        allocator.free(self.body);
    }
};

// Simula um cliente HTTP assíncrono
fn httpGet(allocator: std.mem.Allocator, url: []const u8) HttpError!HttpResponse {
    std.debug.print("[HTTP] GET {s}\n", .{url});
    
    // Em código real, aqui faria I/O não-bloqueante
    // suspend e resume quando dados disponíveis
    std.time.sleep(200 * std.time.ns_per_ms);
    
    // Simula resposta
    const body = try std.fmt.allocPrint(allocator, "Response from {s}", .{url});
    
    return HttpResponse{
        .status = 200,
        .body = body,
    };
}

// Faz múltiplas requisições concorrentes
fn fetchMultiple(allocator: std.mem.Allocator, urls: []const []const u8) !void {
    // Cria frames para cada URL
    var frames = try allocator.alloc(@Frame(httpGet), urls.len);
    defer allocator.free(frames);
    
    // Inicia todas as requisições
    for (urls, 0..) |url, i| {
        frames[i] = async httpGet(allocator, url);
    }
    
    // Aguarda todas completarem
    for (frames, urls) |*frame, url| {
        const response = await frame catch |err| {
            std.debug.print("Erro ao buscar {s}: {}\n", .{url, err});
            continue;
        };
        defer response.deinit(allocator);
        
        std.debug.print("✓ {s}: status {}, body: {s}\n", .{
            url, response.status, response.body,
        });
    }
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    
    const urls = &.{
        "https://api.exemplo.com/users",
        "https://api.exemplo.com/posts",
        "https://api.exemplo.com/comments",
    };
    
    try fetchMultiple(allocator, urls);
}
```

## Error Handling em Async

Erros em código async funcionam de forma elegante com error unions:

```zig
const std = @import("std");

const DatabaseError = error{
    ConnectionLost,
    QueryTimeout,
    RecordNotFound,
};

// Função que pode falhar
async fn fetchUser(id: u32) DatabaseError!User {
    if (id == 0) {
        return DatabaseError.RecordNotFound;
    }
    
    // Simula I/O
    std.time.sleep(50 * std.time.ns_per_ms);
    
    return User{
        .id = id,
        .name = "João",
    };
}

const User = struct {
    id: u32,
    name: []const u8,
};

pub fn main() !void {
    // Propagação de erros funciona normalmente
    const frame1 = async fetchUser(1);
    const user1 = try await frame1;
    std.debug.print("Usuário: {s}\n", .{user1.name});
    
    // Tratando erro
    const frame2 = async fetchUser(0);
    const user2 = await frame2 catch |err| {
        std.debug.print("Erro ao buscar usuário 0: {}\n", .{err});
        return;
    };
    
    std.debug.print("Usuário: {s}\n", .{user2.name});
}
```

### Padrão: Timeout com async

```zig
const std = @import("std");

fn operationWithTimeout(
    comptime F: type,
    frame: F,
    timeout_ms: u64,
) !F.return_type {
    const start = std.time.milliTimestamp();
    
    while (true) {
        // Verifica se completou (simplificado)
        // Em código real, verificaria estado do frame
        
        if (std.time.milliTimestamp() - start > timeout_ms) {
            return error.Timeout;
        }
        
        std.time.sleep(1 * std.time.ns_per_ms);
    }
}
```

## Performance e Considerações

### Quando Usar Async

| Use async quando... | Evite async quando... |
|---------------------|----------------------|
| Muitas conexões I/O (10K+) | Código puramente CPU-bound |
| Latência é crítica | Overhead de state machine não justifica |
| Precisa de controle fino de concorrência | Simplicidade é prioridade |
| Recursos são limitados | Time to market é crítico |

### Comparação de Performance

| Operação | Síncrono | Async | Threads |
|----------|----------|-------|---------|
| **Memória por conexão** | ~0 bytes | ~100 bytes | ~1 MB |
| **Context switch** | N/A | ~50 ns | ~1-10 µs |
| **Escalabilidade** | Baixa | Alta | Média |
| **Complexidade** | Baixa | Média | Média |

### Benchmark Simples

```zig
const std = @import("std");

fn benchmark() void {
    const start = std.time.milliTimestamp();
    
    // Cria 10.000 frames
    var frames: [10000]@Frame(dummyTask) = undefined;
    for (&frames, 0..) |*frame, i| {
        frame.* = async dummyTask(i);
    }
    
    // Aguarda todos
    for (&frames) |*frame| {
        await frame;
    }
    
    const elapsed = std.time.milliTimestamp() - start;
    std.debug.print("10.000 tasks em {}ms\n", .{elapsed});
}

fn dummyTask(id: usize) void {
    _ = id;
    suspend {}
}
```

## Estado Atual do Async em Zig

> ⚠️ **Atenção**: A implementação de async/await em Zig está sendo refinada. Algumas APIs mencionadas podem mudar.

### O Que é Estável

- ✅ Sintaxe `async`/`await`/`suspend`/`resume`
- ✅ Semântica de frames
- ✅ Integração com error handling
- ✅ Composição com structs

### O Que Está Evoluindo

- 🔄 Event loop padrão (`std.event`)
- 🔄 Integração com I/O assíncrono do SO
- 🔄 APIs de networking assíncronas (veja o guia de [networking e sockets em Zig](/tutoriais/zig-networking-sockets/) para uso prático)
- 🔄 Debugging de código async

### Alternativas para Produção

Se você precisa de async estável hoje:

1. **libxev** — Event loop de alta performance
2. **aio** — Abstração de I/O assíncrono
3. **io_uring** (Linux) — Via bindings C

```zig
// Exemplo com libxev (pseudo-código)
const xev = @import("xev");

var loop = xev.Loop.init(.{});
defer loop.deinit();

// Agenda operação de I/O
loop.run();
```

## Padrões Comuns

### 1. Async Iterator

```zig
fn AsyncIterator(comptime T: type) type {
    return struct {
        nextFn: *const fn () anyerror!?T,
        
        pub fn next(self: @This()) !?T {
            return try self.nextFn();
        }
    };
}
```

### 2. Async Channel

```zig
fn Channel(comptime T: type) type {
    return struct {
        queue: std.ArrayList(T),
        mutex: std.Thread.Mutex,
        
        pub fn send(self: *@This(), value: T) !void {
            self.mutex.lock();
            defer self.mutex.unlock();
            try self.queue.append(value);
        }
        
        pub fn receive(self: *@This()) !?T {
            self.mutex.lock();
            defer self.mutex.unlock();
            return self.queue.popOrNull();
        }
    };
}
```

### 3. Semaphore Assíncrono

```zig
const Semaphore = struct {
    permits: usize,
    
    pub fn acquire(self: *Semaphore) void {
        while (self.permits == 0) {
            suspend {}
        }
        self.permits -= 1;
    }
    
    pub fn release(self: *Semaphore) void {
        self.permits += 1;
        // Resume waiters...
    }
};
```

## Resumo

Você aprendeu:

- ✅ **Frames**: Funções async como state machines
- ✅ **Sintaxe**: `async`, `await`, `suspend`, `resume`
- ✅ **Event loops**: Implementação e uso
- ✅ **HTTP assíncrono**: Cliente não-bloqueante
- ✅ **Error handling**: Erros funcionam naturalmente com async
- ✅ **Performance**: Quando e por que usar async

### Checklist

- [ ] Entenda que async ≠ paralelismo
- [ ] Escolha seu event loop (ou implemente)
- [ ] Use `suspend`/`resume` para controle fino
- [ ] Trate erros com error unions
- [ ] Meça antes de otimizar
- [ ] Fique atento a mudanças nas APIs

## Próximos Passos

Continue seu aprendizado:

- 🌐 [Criando um Servidor HTTP em Zig](/tutoriais/zig-http-server/) — Aplique async em servidores web
- 🧪 [Testes em Zig: Guia Completo](/tutoriais/testes-zig/) — Aprenda a testar código async
- 🔄 [Comptime em Zig](/tutoriais/comptime-em-zig/) — Meta-programação com async
- 📚 [libxev documentation](https://github.com/mitchellh/libxev) — Event loop production-ready

---

**Recursos Adicionais**

- [Zig Async Documentation](https://ziglang.org/documentation/master/#Async-Functions)
- [Ziglearn - Concurrency](https://ziglearn.org/chapter-5/)
- [Zig Showtime - Async/await](https://www.youtube.com/watch?v=HsnOS9QzjR8)

*Última atualização: 09 de fevereiro de 2026*  
*Versão do Zig: 0.13.0*  
*Nota: APIs de async sujeitas a mudanças*
