# Await and Suspension

### Await and Suspension

`await` means: wait until an asynchronous operation has finished, then continue with its result.

A suspension point is a place where the current operation may pause so something else can make progress.

In simple words:

```text
async starts work
await waits for work
suspension lets other work run
```

This is the basic shape of asynchronous programming.

#### Start First, Wait Later

A normal function call works like this:

```zig
const value = doWork();
use(value);
```

The program cannot continue until `doWork()` finishes.

Async code separates starting from waiting:

```zig
const future = io.async(doWork, .{});
const value = try future.await(io);
use(value);
```

The first line starts the work.

```zig
const future = io.async(doWork, .{});
```

The second line waits for the result.

```zig
const value = try future.await(io);
```

The value returned by `io.async` is a future. A future is a handle to work that may still be running.

#### What `await` Does

When you call `await`, you are saying:

```text
I need this result before I can continue.
```

If the work is already finished, `await` returns quickly.

If the work is still running, `await` waits.

If the work failed, `await` returns the error.

That is why async code often uses `try`:

```zig
const value = try future.await(io);
```

This means:

```text
wait for the future
if it failed, return the error
otherwise, give me the value
```

#### Await Is a Boundary

Before `await`, the operation may be unfinished.

After `await`, the operation is complete.

```zig
const future = io.async(readFile, .{ path });

// The read may still be in progress here.

const contents = try future.await(io);

// The read is finished here.
```

This boundary is important for ownership.

Before the future completes, you must keep alive anything the operation needs. That includes buffers, paths, pointers, allocators, and shared state.

#### Suspension

A suspension point is where a task may stop temporarily.

In async code, waiting usually creates a suspension point.

```zig
const result = try future.await(io);
```

If the future is not ready, the current task may suspend. The program can then run other work while this task is waiting.

This is the main benefit of async code. Waiting does not have to waste the whole program’s time.

#### Suspension Is Not Random

A beginner may imagine async code can stop anywhere. That would make programs impossible to reason about.

A better mental model is:

Async code may suspend only at known waiting points.

In code using futures, those waiting points are visible:

```zig
try future.await(io);
```

That visible `await` is useful. It tells you where the function may pause.

#### Why Visible Suspension Matters

Suppose you have code like this:

```zig
state.beginUpdate();

const data = try future.await(io);

state.finishUpdate(data);
```

The `await` happens between `beginUpdate` and `finishUpdate`.

That may be a problem. While this task is waiting, other work may run. That other work might observe `state` halfway through an update.

A safer design is often:

```zig
const data = try future.await(io);

state.beginUpdate();
state.finishUpdate(data);
```

Wait first. Then update state.

The rule is:

Avoid holding half-finished state across an `await`.

#### Do Not Hold Locks Across Await

This is especially important with mutexes.

Bad pattern:

```zig
state.mutex.lock();
defer state.mutex.unlock();

const data = try future.await(io);

state.value = data;
```

The lock is held while waiting.

That can block other code for a long time. In worse cases, it can deadlock if the awaited operation needs something that also requires the same lock.

Better:

```zig
const data = try future.await(io);

state.mutex.lock();
defer state.mutex.unlock();

state.value = data;
```

Wait first. Lock only when updating the shared state.

The same principle appeared with threads: keep critical sections small. With async code, this becomes even more important.

#### Awaiting Several Futures

If you start two independent operations, start both before awaiting either one.

Less useful:

```zig
const a = try io.async(readA, .{}).await(io);
const b = try io.async(readB, .{}).await(io);
```

This starts `readA`, waits for it, then starts `readB`.

Better shape:

```zig
const future_a = io.async(readA, .{});
defer future_a.cancel(io) catch {};

const future_b = io.async(readB, .{});
defer future_b.cancel(io) catch {};

const a = try future_a.await(io);
const b = try future_b.await(io);
```

Now both operations are started before waiting for their results.

This gives the I/O backend a chance to overlap them.

#### Await Order Still Matters

Even if you start several futures first, the order of `await` can affect behavior.

```zig
const a = try future_a.await(io);
const b = try future_b.await(io);
```

This waits for `a` first.

If `b` finishes early, the code still waits for `a` before handling `b`.

Sometimes that is fine. Sometimes you want whichever result finishes first. That requires a different structure, usually an event loop, queue, or higher-level I/O pattern.

For beginners, the simple rule is enough:

Start independent work first. Await when you need the result.

#### Cancellation and Await

A future should not be abandoned.

If you start work and then leave the scope, the program must clean up that work.

That is why async examples often use this pattern:

```zig
const future = io.async(doWork, .{});
defer future.cancel(io) catch {};

const result = try future.await(io);
```

The `defer` protects early exits.

If `await` succeeds normally, the future is finished. If an error or early return happens before that, `cancel` gives the operation a chance to stop and release resources.

This is the same Zig habit used elsewhere:

```zig
resource.acquire();
defer resource.release();
```

Async work is a resource too.

#### Await and Errors

A future returns whatever the async operation returns.

If the operation returns an error union, `await` returns that error union too.

For example, suppose the operation has this shape:

```zig
fn readConfig() !Config {
    // may fail
}
```

Then awaiting its future may fail:

```zig
const future = io.async(readConfig, .{});
defer future.cancel(io) catch {};

const config = try future.await(io);
```

The `try` belongs at the await point because that is where the final result is collected.

#### Await and Lifetimes

This is one of the most important rules.

Any data used by the async operation must stay alive until the operation is finished or cancelled.

Bad:

```zig
fn startRead(io: std.Io) !void {
    var buffer: [1024]u8 = undefined;

    const future = io.async(readInto, .{&buffer});
    _ = future;
}
```

The function returns while the operation may still use `buffer`.

Good:

```zig
fn readNow(io: std.Io) !void {
    var buffer: [1024]u8 = undefined;

    const future = io.async(readInto, .{&buffer});
    defer future.cancel(io) catch {};

    _ = try future.await(io);
}
```

Here, `buffer` stays alive until after `await`.

#### Suspension and Local Variables

When a task suspends, its local variables may need to remain available until it resumes.

That is one reason async runtimes and compilers need careful machinery. A normal function call can often store locals on the stack and remove them when the function returns. An async task may pause and resume later.

As a beginner, you do not need to know all implementation details. You do need the practical rule:

Do not assume async work ends just because the current line has finished.

It ends when you await it, cancel it, or otherwise know it has completed.

#### Async Code Should Have Clear Ownership

When writing async code, ask:

Who owns the future?

Who awaits it?

Who cancels it if something goes wrong?

What memory does the operation use?

How long must that memory live?

These questions prevent most beginner async bugs.

A clean function starts async work and finishes responsibility for it in the same scope:

```zig
const future = io.async(doWork, .{});
defer future.cancel(io) catch {};

const result = try future.await(io);
```

A more advanced program may pass futures around, but then ownership must be documented clearly.

#### The Main Rule

`await` is the point where async work becomes a real result.

Before `await`, you have a promise of future completion.

After `await`, you have the value or the error.

Suspension means the current task may pause while waiting. That pause is useful, but it also affects locks, lifetimes, and shared state.

Write async code so the waiting points are easy to see. Do not hold locks across waits. Do not keep half-updated state across waits. Keep every buffer and pointer alive until the future is finished or cancelled.

