Async functions

Async functions are coroutines which return single value. They are declared with usage of async keyword.

Async functions return values like regular functions - via return operator. Its usage rules are the same as in regular functions.

Simplest async function example:

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

In async functions it’s possible to use yield operator without a value. Its usage pauses an async function execution, which may be resumed later with code following yield operator.

Async functions usage

Async function call returns an async function object. It’s possible to start/resume execution of an async fuction object via if_coro_advance operator for it.

Each usage of this operator resumes async function execution, but only if it was not finished yet. Control flow may be passed to a block of if_coro_advance only once - when an async function finished and thus returned a result.

// This async function makes several pauses before returning a result.
fn async SimpleFunc() : i32
{
    yield;
    yield;
    yield;
    return 555444;
}

fn Foo()
{
    auto mut f= SimpleFunc();
    auto mut result= 0;
    // Execute "if_coro_advance" operator until the async function is not finished.
    // 3 full loop iteration will be executed (equal to the number of "yield" operators inside the async function body), a break from the loop will happen at 4th iteration.
    loop
    {
        if_coro_advance( x : f )
        {
            result= x;
            break;
        }
    }
}

It’s important to be careful with usage of if_coro_advance in a loop until a result will be obtained. If an async function object is already finished, if_coro_advance will never return a result and thus the loop will be infinite. In order to avoid this it’s needed to check an async function object if it is already finished before entering the loop with if_coro_advance.

await operator

There is await operator that simplifies async function calls. This operator is a postfix operator that consists of dot (.) and await keyword and may be used for an async function object inside another async function.

await operator works like this: it resumes passed async function execution, if it is finished - extracts its result, else the caller async function pauses its execution and after it will be resumed, control flow will be passed to a code, that again resumes passed async function execution etc., until passed async function execution isn’t finished.

This operator is somewhat equivalent to the following code:

loop
{
    if_coro_advance( x : f )
    {
        // x - await operator result.
        break;
    }
    else
    {
        yield;
    }
}

await operator requires passed value to be an immediate value of an async function type. It’s also necessary that a passed function is not finished yet, otherwise halt will be executed. After obtaining of the execution result passed async function object is destroyed properly.

await operator usage example:

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;
}

In fact await operator is just a way to simplify an async function call from another async function. Where for regular functions just regular call operator is used, for async function call operator with following await operator is used instead.

Async function type

Async function type is a type of an async function object. Async functions return async function-type objects.

Ü has a special syntax for specifying of async function types. It consists of async keyword, optional notation for inner references specification, optional non_sync tag, return type (with/without reference modifier).

type IntAsyncFunc= async : i32; // Simplest async function
var [ [ char8, 2 ], 1 ] return_references[ "0a" ];
type FloatRefAsyncFunc= async'imut' : f32 & @(return_references); // An async function that returns a reference and stores references inside.
type NonSyncRefAsyncFunc= async'mut' non_sync : u64 &mut @(return_references); // non_sync async function that returns immutable reference and stores mutable references inside.

As it can be seen async function type isn’t strictly affected by the details of a specific async function (by which it was created). This allows to use the same variable for storing of async function object produced by calls to different async functions - with different bodies and parameters.

 // Async functions. Their return type is (async : i32).
fn async Foo(i32 x, i32 y) : i32;
fn async Bar() : i32;
 // A function which returns async function object but which is not async.
fn CreateFunc(bool cond) : (async : i32)
{
    return select(cond ? Foo( 14, 56 ) : Bar() );
}