Асинхронные функции

Асинхронные функции - это корутины, которые возвращают одно значение. Объявляются они с использованием ключевого слова async.

Возвращают значение асинхронные функции так же, как и обычные - через оператор return. Правила его использования такие же, как и в обычных функциях.

Пример простейшей асинхронной функции:

fn async DoubleIt(u32 x) : u32
{
    return x * 2u;
}

В асинхронных функциях можно использовать оператор yield без значения. Его использование приостанавливает выполнение асинхронной функции, которое может быть далее продолжено с кода, следующего после yield.

Использование асинхронных функций

Вызов асинхронной функции возвращает значение объекта-асинхронной функции. Запустить/возобновить выполнение асинхронной функции и получить результат можно с использованием оператора if_coro_advance для этого объекта.

Каждое использование этого оператора возобновляет выполнение асинхронной функции, если только она уже не завершилась. Блоку внутри if_coro_advance управление может передаться лишь однажды - когда асинхронная функция завершилась и тем самым вернула результат.

// Данная асинхронная функция делает несколько остановок, прежде чем вернуть результат.
fn async SimpleFunc() : i32
{
    yield;
    yield;
    yield;
    return 555444;
}

fn Foo()
{
    auto mut f= SimpleFunc();
    auto mut result= 0;
    // Выполняем оператор if_coro_advance до тех пор, пока асинхронная функция не завершится.
    // Будет выполнено 3 полных итерации цикла (по числу yield операторов в асинхронной функции), на 4-й итерации произойдёт выход из цикла.
    loop
    {
        if_coro_advance( x : f )
        {
            result= x;
            break;
        }
    }
}

Стоит быть внимательным с использованием if_coro_advance в цикле до получения результата. Если объект асинхронной функции уже ранее завершился, if_coro_advance уже никогда не вернёт результат, и тем самым цикл может быть бесконечным. Чтобы такого избежать, стоит проверять корутины на завершённость, прежде чем входить в цикл с if_coro_advance.

оператор await

Для упрощения вызова асинхронных функций существует также оператор await. Этот оператор является постфиксным, состоит из точки (.) и ключевого слова await и может быть использован для объекта-асинхронной функции внутри другой асинхронной функции.

Данный оператор работает следующим образом: он возобновляет выполнение асинхронной функции, если она завершилась - извлекается результат, иначе вызывающая асинхронная функция приостанавливается и управление после её возобновления будет передано коду, который снова возобновляет выполнение переданной асинхронной функции и т. д. пока выполнение переданной асинхронной функции не завершится.

Во многом этот оператор эквивалентен следующему коду:

loop
{
    if_coro_advance( x : f )
    {
        // x - результат оператора await.
        break;
    }
    else
    {
        yield;
    }
}

Оператор await требует, чтобы переданное значение было непосредственным значением типа объекта-асинхронной функции. Также необходимо, чтобы переданная асинхронная функция ещё не завершилась, иначе произойдёт halt. После извлечения результата значение асинхронной функции разрушается должным образом.

Пример использования оператора await:

fn async Foo( i32 x ) : i32;

fn async Bar( i32 x, i32 y ) : i32
{
    auto foo_res= Foo( x * y ).await;
    return foo_res / 3;
}

По сути оператор await является способом упрощения вызова одной асинхронной функции из другой. Там, где для обычных функций просто используется оператор вызова, для асинхронных функций используется оператор вызова с последующим оператором await.

Тип асинхронной функции

Тип асинхронной функции - это тип объекта-асинхронной функции. Асинхронные функции возвращают объекты типа асинхронных функций.

В Ü существует специальный синтаксис для указания типа асинхронной функции. Состоит он из ключевого слова async, опциональной нотации для указания внутренних ссылок, опционального non_sync тега, типа (с учётом ссылочности) возвращаемого значения.

type IntAsyncFunc= async : i32; // Простейшая асинхронная функция
var [ [ char8, 2 ], 1 ] return_references[ "0a" ];
type FloatRefAsyncFunc= async'imut' : f32 & @(return_references); // Асинхронная функция, возвращающая ссылку и хранящая внутри себя ссылки.
type NonSyncRefAsyncFunc= async'mut' non_sync : u64 &mut @(return_references); // non_sync асинхронная функция, возвращающая изменяемую ссылку и хранящая внутри себя изменяемые ссылки.

Как можно заметить, тип объекта-асинхронной функции не определяется конкретными особенностями конкретной асинхронной функции (как она была создана). Это позволяет использовать одну и ту же переменную для хранения объектов-асинхронных функций, порождённых разными асинхронными функциями с разным телом и разными параметрами.

// Асинхронные функции. Тип их возвращаемого значения - (async : i32).
fn async Foo(i32 x, i32 y) : i32;
fn async Bar() : i32;
// Функция, возвращающая значения типа асинхронной функции, но сама не являющаяся асинхронной.
fn CreateFunc(bool cond) : (async : i32)
{
    return select(cond ? Foo( 14, 56 ) : Bar() );
}