# Tasks, spawn, and async

> Run concurrent work, wait for results, and compose asynchronous operations.

Canonical: https://alice-lang.org/documentation/concurrency/tasks/

[Documentation index](https://alice-lang.org/documentation/llms.txt) · [Complete book](https://alice-lang.org/llms-full.txt)

## Choose the kind of work

Use `async` and `await` for operations that can wait without blocking a thread, such as asynchronous I/O. Use `spawn` for independent task work. Use numeric pipelines or `Par` for CPU data parallelism. Concurrency and CPU parallelism solve different problems.

## Async methods

```alice
async def twice(x : Int32) : Int32
  await Task.Delay(1)
  x * 2
end
async def main_async
  result = await twice(4)
  puts result
end
main_async.wait
```

Expected output:

```text
8
```

[Edit and run in the Alice playground](https://playground.alice-lang.org/#code=u.YXN5bmMgZGVmIHR3aWNlKHggOiBJbnQzMikgOiBJbnQzMgogIGF3YWl0IFRhc2suRGVsYXkoMSkKICB4ICogMgplbmQKYXN5bmMgZGVmIG1haW5fYXN5bmMKICByZXN1bHQgPSBhd2FpdCB0d2ljZSg0KQogIHB1dHMgcmVzdWx0CmVuZAptYWluX2FzeW5jLndhaXQ)

An `async def` returns a task. The source return annotation describes its result, while the generated CLR return type is `Task(T)` (or `Task` for no result). `await` gets the operation's result while allowing the caller's async execution to resume later.

Assigning the awaited value before printing also makes the evaluation order clear. A method with `await` must be in an appropriate async context.

## Waiting at an entry point

The example calls `main_async.wait` from the synchronous entry point. Within another async method, prefer `await main_async` rather than synchronously blocking with `.wait`. Exceptions in the task propagate when its result is observed.

`Task.Delay` uses milliseconds, while the prelude's synchronous `sleep` helper uses seconds. Pick the appropriate operation rather than carrying units between them accidentally.

## Spawn a task

`task = spawn { ... }` starts concurrent work and returns a task handle. Keep the handle and wait for completion when the program must observe the work's result or failure. Use a channel for communication instead of writing a captured scalar.

```alice
channel = Channel(Int32).new
task = spawn { channel.send(42) }
puts channel.receive
task.wait
```

Do not rely on background work surviving the application's exit.

## What can be captured?

Reading a captured value is allowed. Assigning a captured local or mutating shared objects from a concurrent context may be rejected as a data race. A task boundary does not clone captured objects or make their mutations safe. Read [race safety](https://alice-lang.org/documentation/concurrency/race-safety.md) before moving sequential code into a task.

## Cancellation and libraries

.NET asynchronous APIs can take `CancellationToken` and expose their normal Task/ValueTask contracts to Alice. Pass cancellation through the APIs involved, and observe task failures. `spawn` and `async` do not automatically make an arbitrary blocking method cancellable.

## Continue reading

- Previous: [SIMD, parallel, and sparse math](https://alice-lang.org/documentation/library/numerics.md)
- Next: [Channels and actors](https://alice-lang.org/documentation/concurrency/channels.md)
