Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. Zig Dictionary
  3. Async / Await (Current Status)

Async / Await (Current Status)

Zig once had cooperative async processing (coroutines) built into the language via the async / await / suspend / resume keywords, but these features have been temporarily disabled since Zig 0.11.0. The async implementation became too complex to maintain during the compiler's self-hosting redesign. Re-designed async support is planned for a future version, but as of the time of writing (March 2026) no timeline has been confirmed. For concurrent processing today, the practical options are OS threads via std.Thread or a community async library such as libxev.

The old async / await syntax (historical reference)

// -----------------------------------------------
// Note: The following syntax applies to Zig 0.10 and earlier.
//       It produces a compile error on Zig 0.11 and later.
//       Included here as a historical reference.
// -----------------------------------------------

const std = @import("std");

// Declaring an async function (0.10 and earlier)
fn fetchHp() callconv(.Async) i32 {
    suspend {}   // Returns control to the caller
    return 1500;
}

pub fn main() void {
    // async creates a frame; await waits for its result
    var frame = async fetchHp();
    const hp = await frame;
    std.debug.print("HP: {d}\n", .{hp});
}

Summary of async / await / suspend concepts

KeywordRoleStatus (Zig 0.11+)
asyncCreates a frame for an async function and starts execution.Disabled. Produces a compile error.
awaitWaits for a frame to complete and retrieves its return value.Disabled. Produces a compile error.
suspendPauses the current frame and returns control to the caller.Disabled. Produces a compile error.
resumeResumes a suspended frame.Disabled. Produces a compile error.
nosuspendAsserts that an expression will not suspend.Disabled. Produces a compile error.

Current alternative: threads via std.Thread

To run multiple operations concurrently in Zig 0.11 or later, use std.Thread. It creates OS-native threads, so concurrency is preemptive rather than cooperative.

kof_thread.zig
// kof_thread.zig — Demonstrates the basics of std.Thread using KOF characters
//
// How to build:
//   zig run kof_thread.zig

const std = @import("std");

// -----------------------------------------------
// Struct for arguments passed to each thread
// -----------------------------------------------

const FighterArgs = struct {
    name: []const u8,   // Fighter name
    power: u32,         // Combat power
    delay_ms: u64,      // Simulated delay (milliseconds)
};

// -----------------------------------------------
// Function run on each thread
// -----------------------------------------------

fn trainFighter(args: FighterArgs) void {
    // Simulates processing time (equivalent to a real I/O wait)
    std.time.sleep(args.delay_ms * std.time.ns_per_ms);
    std.debug.print(
        "  [{s}] Training complete: power {d}\n",
        .{ args.name, args.power },
    );
}

// -----------------------------------------------
// Main function: spawns multiple threads to run concurrently
// -----------------------------------------------

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    try stdout.print("=== KOF Async Training (std.Thread) ===\n\n", .{});

    // Train each fighter on a separate thread
    const kyo_args    = FighterArgs{ .name = "Kyo Kusanagi",  .power = 9800,  .delay_ms = 30 };
    const iori_args   = FighterArgs{ .name = "Iori Yagami",   .power = 9900,  .delay_ms = 10 };
    const leona_args  = FighterArgs{ .name = "Leona",         .power = 8700,  .delay_ms = 20 };
    const benimaru_args = FighterArgs{ .name = "Daimon Goro", .power = 8500,  .delay_ms = 25 };

    // Spawn threads (execution begins immediately on spawn)
    const t_kyo    = try std.Thread.spawn(.{}, trainFighter, .{kyo_args});
    const t_iori   = try std.Thread.spawn(.{}, trainFighter, .{iori_args});
    const t_leona  = try std.Thread.spawn(.{}, trainFighter, .{leona_args});
    const t_beni   = try std.Thread.spawn(.{}, trainFighter, .{benimaru_args});

    // Wait for all threads to finish (without join, threads may not complete before the process exits)
    t_kyo.join();
    t_iori.join();
    t_leona.join();
    t_beni.join();

    try stdout.print("\nAll fighters have completed their training.\n", .{});
}
zig run kof_thread.zig
=== KOF Async Training (std.Thread) ===

  [Iori Yagami] Training complete: power 9900
  [Leona] Training complete: power 8700
  [Daimon Goro] Training complete: power 8500
  [Kyo Kusanagi] Training complete: power 9800

All fighters have completed their training.

Preventing data races with Mutex

When multiple threads read and write shared data simultaneously, a data race can occur. Use std.Thread.Mutex to protect the critical section.

kof_mutex.zig
// kof_mutex.zig — Updates a score counter in a thread-safe way using Mutex
//
// How to build:
//   zig run kof_mutex.zig

const std = @import("std");

// -----------------------------------------------
// Shared scoreboard (written to by multiple threads simultaneously)
// -----------------------------------------------

const ScoreBoard = struct {
    mutex: std.Thread.Mutex = .{},   // Mutex for locking
    total: u32 = 0,                  // Total score

    // Adds a score in a thread-safe manner
    fn add(self: *ScoreBoard, name: []const u8, score: u32) void {
        self.mutex.lock();
        defer self.mutex.unlock();   // defer guarantees the unlock happens

        self.total += score;
        std.debug.print("  [{s}] +{d}pt → total: {d}pt\n", .{ name, score, self.total });
    }
};

// -----------------------------------------------
// Function run on each thread
// -----------------------------------------------

const AddArgs = struct {
    board: *ScoreBoard,
    name: []const u8,
    score: u32,
};

fn addScore(args: AddArgs) void {
    // Adds the score 3 times to accumulate the total
    for (0..3) |_| {
        args.board.add(args.name, args.score);
        std.time.sleep(5 * std.time.ns_per_ms);
    }
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    try stdout.print("=== KOF Scoreboard (Mutex) ===\n\n", .{});

    var board = ScoreBoard{};

    const t1 = try std.Thread.spawn(.{}, addScore, .{.{ .board = &board, .name = "Kyo Kusanagi",  .score = 500 }});
    const t2 = try std.Thread.spawn(.{}, addScore, .{.{ .board = &board, .name = "Iori Yagami",   .score = 600 }});
    const t3 = try std.Thread.spawn(.{}, addScore, .{.{ .board = &board, .name = "Terry Bogard",  .score = 450 }});

    t1.join();
    t2.join();
    t3.join();

    try stdout.print("\nFinal score: {d}pt\n", .{board.total});
}
zig run kof_mutex.zig
=== KOF Scoreboard (Mutex) ===

  [Kyo Kusanagi] +500pt → total: 500pt
  [Iori Yagami] +600pt → total: 1100pt
  [Terry Bogard] +450pt → total: 1550pt
  [Kyo Kusanagi] +500pt → total: 2050pt
  [Terry Bogard] +450pt → total: 2500pt
  [Iori Yagami] +600pt → total: 3100pt
  [Kyo Kusanagi] +500pt → total: 3600pt
  [Iori Yagami] +600pt → total: 4200pt
  [Terry Bogard] +450pt → total: 4650pt

Final score: 4650pt

External library: async I/O with libxev

Library / FeatureDescriptionUse case
libxevAn event-loop-based async I/O library. Uses io_uring (Linux), kqueue (macOS), or IOCP (Windows) as its backend.High-performance async processing such as network servers, timers, and file I/O.
Zig async redesign issueThe official tracking issue for the async redesign. Contains discussion on the current status and design direction.Checking for the latest information.
std.ThreadCreates OS-native threads with no additional dependencies.CPU-bound parallel processing or simple concurrent tasks.
std.Thread.MutexProtects a critical section.Serializing access to shared data from multiple threads.
std.Thread.SemaphoreA semaphore that limits the number of concurrent accesses.Connection pools and capping the number of simultaneous operations.

Summary

Zig's async / await / suspend were designed as coroutine-based cooperative async processing, but they have been temporarily disabled since Zig 0.11 as a consequence of the compiler's self-hosting redesign. The feature is expected to return in an improved form in a future version. For concurrent and parallel processing today, the recommended approach is to spawn threads with std.Thread and prevent data races with std.Thread.Mutex or std.Thread.Semaphore. For high-performance async I/O, combining a community library such as libxev is a practical option. For allocators used to manage threads and memory safely, see Allocator Basics. For combining async with error handling, see Error Handling.

If you find any errors or copyright issues, please .