Programming‎ > ‎Nemerle‎ > ‎

AsyncAwait

Introduction

In the first month of learning Nemerle language I wrote AsyncAwait macro which is an implementation of async/await keywords known from C#. There is a similar approach for asynchronous programming in Nemerle - Computation Expression (which was a starting point for me), but it is not compatible with C#'s Task-based Asynchronous Pattern. You can use my solution to integrate with code written in C#.

You can find excellent introduction to the "Task-based Asynchronous Pattern" here:
Reading this document before using my macro is highly recommended.

Features

  • compatible with C# 5.0 Task-based Asynchronous Pattern
  • syntax resembles that of C#
  • .NET 4.5 and VS2012 are not required (works fine with .NET 4.0 and VS2010 with AsyncTargetingPack.NET4)
  • support for the following language constructs: do / while / for / foreach / try / catch / finally / using
  • support for GetAwaiter
  • support for ConfigureAwait
  • support for IProgress
  • support for CancellationToken

Usage

You need to:
  • add Macro Reference to Nemerle.Async.Macros assembly
  • add normal reference to Nemerle.Async assembly
  • add Nemerle.Async using to your source code file

Syntax is very similar to that known from C#:

C# and Nemerle example

C#:

public async Task<int> AsyncMethod(int a)
{
    int res = await AnotherMethod(a);
    return res + 10;
}

Nemerle:

public async AsyncMethod(a : int) : Task[int]
{
    def res = await AnotherMethod(a);
    res + 10;
}

You can also use constructions not possible with C#:

Async function

def proc(cur, max)
{
    async
    {
        def res = await fib(10);
        textBox1.Text = $"fib(10) = $res\n";
    }
}

Async block defined inside method body

await async
{
    def res = await fib(10);
    textBox1.Text = $"fib(10) = $res\n";
}

Exception Handling

What I don't like in TAP pattern is that's very easy to miss uncaught exception. The same applies to Nemerle's and C#'s implementation, so there is no difference here but it deserves a mention. Unhandled exceptions are rethrown outside Task encapsulation (and can cause application crash for example) only in the case of async handlers (async methods that return void):

Unhandled exception

public async OnButtonClick() : void
{
    throw Exception("Unhandled user exception!");
}
In every other situation exceptions are encapsulated inside Task's Exception property and won't rethrow it if not checked explicitly. For example, calling fallowing method won't crash your application:

It won't crash

public Test() : void
{
    _ = async
    {
        throw Exception("It won't crash!");
    }
}
Because of that I recommend always to use try / catch statements when calling asynchronous code:

Always use try/catch when calling asynchronous code

public async Test() : Task
{
    try
    {
        await async
        {
            throw Exception("This time it will be caught!");
        }
    }
    catch
    {
        | _ => .... exception caught ....
    }
}

Deficiencies

In the current version there are few gaps. I don't know if I will ever fill them. The reason of this is they are minor for me:
  • current solution is not optimized at all and uses closures, so it is slower than C#'s implementation and allocates more memory (rewriting to state machine is a tremendous work and I'm not considering it now), but if you will use it wisely, it should be no problem for you
  • block statements are not supported
  • you cannot use await as a parameter for a function/method call
  • the output program has a dependency of "Nemerle.Async" assembly

Download

You can download the latest version from download page. It is also part of snippets for Nemerle. You can download it with Nemerle source code.

Comments