- Antril
- Referencia
- Iteradores y código asíncrono
Esta página aún no está traducida al español. Leer la versión en inglés
Iterators and async
How loop walks collections, functions that yield their values one at a time, async functions, await and tasks.
En esta página
What loop walks
loop (T x in c) works on:
c |
What x takes |
|---|---|
| An array, of any number of dimensions | Each element, in row order. |
A str |
Each code unit (a u8: str is UTF-8). |
Any value with an Iterator() method |
What the iterator gives. |
For the last kind, the compiler calls c.Iterator() once, then, while the iterator's Next() returns true, reads its Current property into x and runs the body. There is no interface to implement: any type with those three members works, which is how List<T>, Dictionary<K, V>, HashSet<T> and the other collections are walked.
import System;
public class Countdown
{
i32 from;
public:
new(i32 from) { this.from = from; }
Iterator() : CountdownIterator => new CountdownIterator(from);
}
public class CountdownIterator
{
i32 next;
i32 current;
public:
new(i32 start) { next = start; }
Next() : bool
{
if (next < 0)
return false;
current = next;
next--;
return true;
}
i32 Current { get => current; }
}
Main()
{
loop (i32 n in new Countdown(3))
Console.Write(n + " ");
Console.WriteLine();
loop (u8 unit in "hé")
Console.Write(unit + " ");
Console.WriteLine();
}
larva run
3 2 1 0
104 195 169
é is two units in UTF-8. The loop variable's type converts from Current's type as an assignment would.
Functions with yield
A function, method or static method whose result is Iterator<T> can give its values one at a time:
| Statement | What it does |
|---|---|
yield value; |
Gives the next value and stops there until the next one is asked. |
yield break; |
Ends: no more values. |
return; |
Ends too, like reaching the end of the body. |
import System;
public class Bag
{
List<str> items = new List<str>();
public:
Add(str s) { items.Add(s); }
Each() : Iterator<str>
{
loop (str s in items)
{
if (s == "stop")
yield break;
yield s;
}
}
}
Evens(i32 limit) : Iterator<i32>
{
loop (i32 i = 0..limit)
if (i % 2 == 0)
yield i;
}
Main()
{
Bag b = new Bag();
b.Add("a");
b.Add("b");
b.Add("stop");
b.Add("c");
loop (str s in b.Each())
Console.Write(s);
Console.WriteLine();
Iterator<i32> it = Evens(7);
it.Next();
Console.WriteLine(it.Current);
loop (i32 e in it)
Console.Write(e + " ");
Console.WriteLine();
}
larva run
ab
0
2 4 6
- Calling the function runs nothing yet: it gives a
System.Iterator<T>. EachNext()runs the body on to its nextyieldand returnstrue, or returnsfalseonce the body has ended (and from then on).Currentis the value the lastyieldgave. Iterator()on an iterator gives itself, so a loop over an iterator that was already advanced walks what is left of it.- An iterator runs once. Call the function again to start over.
- The function's parameters, and
thisin a method, are kept in the iterator object for as long as it lives.
| Code | When |
|---|---|
| AN2110 | A function with yield does not give an Iterator<T>. |
| AN2111 | yield is inside a lambda (it belongs to a function's own body). |
| AN2112 | A function with yield returns a value: it ends with yield break; or return;. |
| AN2113 | yield is inside a catch. |
async functions
A function or method marked async gives a Task (no result) or a Task<T> (a result of type T), both in System.Threading.Tasks. Inside it, await task waits for another task without blocking the others, and is the task's result (nothing, for a Task).
import System;
import System.Threading.Tasks;
public class Downloader
{
str name;
public:
new(str name) { this.name = name; }
async Get(i32 size) : Task<str>
{
await Task.Delay(5);
return name + ":" + size;
}
}
async Twice() : Task<i32>
{
i32 a = await Task<i32>.FromResult(20);
i32 b = await Task.Run<i32>(() => 1 + 1);
return a + b;
}
async Loud() : Task
{
await Task.Yield();
throw new InvalidOperationError("boom");
}
Main()
{
Downloader d = new Downloader("file");
Task<str> t = d.Get(3);
Console.WriteLine(t.IsCompleted);
Console.WriteLine(t.Result);
Console.WriteLine(Twice().Result);
Task bad = Loud();
try
{
bad.Wait();
}
catch (InvalidOperationError e)
{
Console.WriteLine("caught " + e.Message + " " + bad.IsFaulted);
}
}
larva run
false
file:3
22
caught boom true
asyncgoes on free functions, static functions and methods of classes and values. The body runs until its firstawaitof a task that is not complete, and the caller gets the task at that point.return value;in an async function completes itsTask<T>; a plainTaskreturns nothing.Task<T>needs aTthat can be copied (T : copy).- An exception thrown in an async function is kept in its task, which is then faulted.
await,Wait()andResultthrow it again, as it was.
| Code | When |
|---|---|
| AN2114 | An async function gives something other than Task or Task<T>. |
| AN2115 | await outside an async function, or in a lambda inside one. |
| AN2116 | await inside a catch. |
| AN2117 | await on something that is not a Task or Task<T>. |
| AN1026 | An async function without a body. |
Tasks
| Member | What it does |
|---|---|
Task.Delay(ms) |
A task that completes after ms milliseconds. |
Task.Yield() |
Lets the other ready tasks run before continuing. |
Task.WhenAll(tasks) |
A task that completes when every task of the array has. |
Task.CompletedTask |
A Task that is already complete. |
Task<T>.FromResult(value) |
A Task<T> that is already complete with value. |
Task.Run(f), Task.Run<T>(f) |
Runs the function f on the thread pool: a task that completes when it returns (faulted if it throws). |
t.Wait() |
Blocks until t completes; throws its exception if it failed. |
t.Result |
Waits, then gives the value of a Task<T>. |
t.IsCompleted, t.IsFaulted |
Whether it has finished, and whether it failed. |
The scheduler
Async functions take turns on one thread. System.Threading.Tasks.Scheduler runs the ready tasks in order, then the timers that are due, and sleeps when nothing is ready until the next timer. Wait() and Result run the scheduler until their task is complete, which is how a program that is not async itself, such as Main, drives its tasks:
import System;
import System.Threading.Tasks;
async Tick(str name, i32 times) : Task
{
loop (i32 i = 0..times)
{
Console.WriteLine(name + " " + i);
await Task.Yield();
}
}
Main()
{
Task[] both = [Tick("a", 2), Tick("b", 2)];
Task.WhenAll(both).Wait();
}
larva run
a 0
b 0
a 1
b 1
Only Task.Run uses other threads; its result comes back to the scheduler's thread. For other work on several threads, see System.Threading in the standard library.
Streams
System.IO streams (FileStream, MemoryStream, BufferedStream, StreamReader, StreamWriter) are synchronous.
Warning
Not yet available: asynchronous stream members (ReadAsync, WriteAsync, CopyToAsync…). Wrap a synchronous call in Task.Run to move it off the scheduler's thread.
Next: Reactive values.
Editar esta página en GitHub Reportar un problema