BetterAsync

In this project, I decided to rewrite some of Microsoft’s implementations of asynchronous classes.

There are normally to types of method/function – synchronous and asynchronous. However, this is a bit confusing. Code will normally run synchronously – which literally means ‘existing or occurring at the same time’ (thanks google). However, should you write the following code:

//C# 'synchronous' code

Console.WriteLine("Hello 1");
Console.WriteLine("Hello 2");
Console.WriteLine("Hello 3");

/*
*  Output:
*  Hello 1
*  Hello 2
*  Hello 3
*/

… you would find that the code would print one after the other, or in my opinion asynchronously – ‘not existing or occurring at the same time’. Should it be truly synchronous I think the order of execution should be indeterminate – basically random based on the environment. However, things get a little clearer when we see an async function:

async void DoStuff() {
    //waits for SomethingThatTakesTime before continuing.
    Console.WriteLine(await SomethingThatTakesTime());

    SomethingThatTakesTimeV2(); //what?!
    Console.WriteLine("Finished doing stuff");
}

As you may have figured, things get a little strange and ambiguous around line 3, and this is why Microsoft has implemented Tasks – with the same goal as JS Promises, but very different still.

I (and maybe you too!) find this extremely confusing, so I decided to write my own asynchronous library – starting with promises. Promises are similar to real promises – the code will wait for a resource while “promising” to get back to you. Here is an example.

//JS
function promiseMe() {
    return new Promise((resolve,reject) => {
        wasteTime();
        resolve(3);
    )
}

//waits until resolve() is called and then returns with that value
let a = await promiseMe();

console.log(a); //3
promiseMe(); //starts executing in another thread, so code below is not halted

This is subtly different to Tasks, but in my opinion a far better way of doing stuff, based on one thing – the resolve and reject functions. The fact that these are parameters means that they can be called from anywhere – something that I don’t think Tasks can do. My promise class for C# behaves mostly the same – although I haven’t shown everything possible here.

After this, I decided to implement ways of communicating between threads – flags and channels. My version of flags are basically super powered booleans – with events for raising and lowering and functions for waiting for raising.

Channels are different however. You may have heard of channels from Go:

//Go
package main
import "fmt"

func sum(s []int, c chan int) {
    sum := 0
    for _, v := range s {
        sum += v
    }
    c <- sum //send sum to c
}

func main() {
    s := []int{7, 2, 8, -9, 4, 0}

    c := make(chan int)
    go sum(s[:len(s)/2], c)
    go sum(s[len(s)/2:], c)
    x, y := <-c, <-c //receive from c

    fmt.Println(x, y, x+y)
}

As you may be able to make out, Channels are like Queues which upon “Dequeuing” will behave normally unless the Channel is empty. If the channel is empty, execution halts until the channel has an item in it. Of course, my C# implementation had to be slightly tweaked as Go is an asynchronous? language (I think!).

I also implemented a simplified version of a thread, which I names a Flow.

For this project I also added docstrings – which describe the purpose of things to make development easier. I thoroughly enjoyed working on this and may continue it in the future. The project is available on Github

View project

Leave a Reply

Your email address will not be published.