Async and reactive code
async functions, await and tasks, and reactive fields, computed values and effects in Antril.
On this page
async and await
A function marked async returns a Task (no result) or a Task<T>. Inside it, await waits for another task without blocking the others; the function continues where it stopped when that task completes.
import System;
import System.Threading.Tasks;
into namespace Jobs
{
async Fetch(i32 id) : Task<i32>
{
await Task.Delay(10);
return id * 10;
}
async Total() : Task<i32>
{
i32 a = await Fetch(1);
i32 b = await Fetch(2);
return a + b;
}
async Tick(str name, i32 times) : Task
{
loop (i32 i = 0..times)
{
Console.WriteLine(name + " " + i);
await Task.Yield();
}
}
Main()
{
Console.WriteLine(Total().Result);
Task[] both = [Tick("a", 2), Tick("b", 2)];
Task.WhenAll(both).Wait();
}
}
larva run
30
a 0
b 0
a 1
b 1
asyncgoes on free functions and on methods of classes.awaitis only allowed inside anasyncfunction.Task<T>needs aTthat can be copied (T : copy).- Async functions take turns on one thread:
System.Threading.Tasks.Schedulerruns whatever is ready, and the timers. Wait()blocks until a task completes, andResultwaits and gives its value. Use them where you are not in anasyncfunction, such asMain.
Task member |
What it does |
|---|---|
Task.Delay(ms) |
A task that completes after ms milliseconds. |
Task.Yield() |
Lets other tasks run before continuing. |
Task.WhenAll(tasks) |
A task that completes when all of tasks have. |
Task<T>.FromResult(value) |
A Task<T> that is already complete. |
Task.Run(f), Task.Run<T>(f) |
A task that runs the function f. |
Wait(), Result, IsCompleted, IsFaulted |
Waiting for a task and asking about it. |
Exceptions
An exception thrown in an async function is kept in its task. await, Wait() and Result throw it again, as it was:
import System;
import System.Threading.Tasks;
async Failing() : Task<i32>
{
await Task.Yield();
throw new InvalidOperationError("no data");
}
async Rescue() : Task<i32>
{
try
{
return await Failing();
}
catch (InvalidOperationError e)
{
Console.WriteLine("failed: " + e.Message);
return -1;
}
}
Main()
{
Console.WriteLine(Rescue().Result);
}
larva run
failed: no data
-1
For work on several threads at once, see System.Threading in the library overview.
Reactive code
Classes can hold reactive state: fields that notice when they are read and changed, so code that depends on them runs again by itself.
| Declaration | What it is |
|---|---|
reactive i32 count = 0; |
A field that is a signal. Reading it inside an effect makes the effect depend on it; assigning it runs those effects again. |
computed i32 Double => count * 2; |
A value computed from others. Whoever reads it depends on what it reads. |
effect { … } |
Runs now and again each time something it read changes. It gives a System.Reactive.Effect; Dispose() stops it. |
reacts Log() { … } |
A method that runs as an effect once its object is constructed. |
import System;
import System.Reactive;
into namespace Ui
{
public class Cart
{
public:
reactive i32 items = 0;
reactive f64 price = 2.5;
computed f64 Total => items * price;
reacts Log()
{
Console.WriteLine("items: " + items);
}
}
Main()
{
Cart cart = new Cart();
Effect shown = effect { Console.WriteLine("total: " + cart.Total); };
cart.items = 2;
Reactive.Batch(() => { cart.items = 3; cart.price = 4.0; });
shown.Dispose();
cart.items = 10;
}
}
larva run
items: 0
total: 0
items: 2
total: 5
items: 3
total: 12
items: 10
Reactive.Batch(f)runsfand then each affected effect once, instead of once per change.- An effect does not run again while it is running, so an effect that writes what it reads does not loop.
- An effect sees the local variables it uses as they are when it runs, like a lambda.
- Reactive members belong to classes, not to
valuetypes.
Next: the reference, which describes every construct of the language in detail.
Edit this page on GitHub Report an issue