Hacker News new | ask | show | jobs
by philwelch 2218 days ago
I don’t really want to implement async/await in terms of channels. I would rather just use channels directly. Notice that even in your Go code it’s explicit which part executes as a separate coroutine and where the blocking communication between coroutines takes place. Async/await is more “magical”, or maybe I’m just dumb.
1 comments

Hmm, I think it only looks magical because I chose a relatively unfair comparison, I realize now.

To show a more realistic comparison, let's asume Bar() is not "async ready" in either Go or C#. Then, the C# code would look something like this:

    async Task<int> Foo() { 
       var task = Task.Run(() => Bar());
       int i = await task;
       return i + 1;
    }
This compares more clearly to the Go version with the same assumption:

    func Foo(ret chan int) {
        task := make(chan int, 1)
        go func() {
             task <- Bar()
        }
        i := <-task
        ret <- i + 1
    }
So the readability / cleanliness is pretty similar for simple examples. However, once you start doing more complex things, like I showed in my second example, the difference become much more pronounced. Overall, a `Task<T>` offers much more information to callers than a `chan T`, so it can usually be composed in more interesting ways, but it can also sometimes be more cumbersome to use.

And again, in both languages, there are clear demarcations between async functions and blocking functions. In C#, async functions return `Task<T>` and are usually marked `async`, while in Go async functions have one or more `chan T` arguments and don't normally return values. If you want to call an async function from a non-async function or vice-versa, you need to use special constructs (e.g. Task.Run, Task.Wait or Task.Result for C#, and `go func () {...}` and blocking channel reads/writes in Go).