This project is read-only.

Taking async/await a step further

Topics: C# Language Design
May 7, 2014 at 10:36 AM
Are there any plans to extend the c# async/await pattern within Roslyn to:
  • Properties
  • Indexer
  • Constructors
    Would be great to get some feedback on this topic.
Thanks
May 7, 2014 at 5:50 PM
Well, how do you propose those would work and in what cases do you think they would be useful?

Here is my opinion on why async constructors would be confusing on SO.

And regarding properties (and indexers), I think making them async also doesn't make sense logically: a property like person.Name should return a string, not Task<string> that has to be awaited. If you need to make it async, I think a method person.GetNameAsync() makes much more sense.
May 7, 2014 at 8:47 PM
Edited May 7, 2014 at 8:51 PM
The async state machine and scaffolding as generated by async/await take a respectable (while mostly not harmful) amount of extra code to be generated. (Stephen Toub's Zen of async presentation has more on this.) Properties and indexers are supposed to be brief, or at least as long as they have to be, but not trip-to-the-soda/coffee-machine-length. Constructors are also not expected to run for a long time. There's no culture in the language of these things taking so long you'd have to put off this work somewhere else. And most of the time, these things are at least currently CPU-bound.

If they were brief enough that they would be unsurprising properties, indexers and constructors, the async state machine would introduce allocations and overhead. Also, Stephen warns about making immediate CPU-bound activity into async methods - that takes things that could get done now and adds code to it. As I understand it, if it is CPU-bound and you don't want to hog the dispatcher/synchronization context/message loop, you are supposed to await Task.Run to run that code elsewhere while giving others the floor.

And on the other hand, if they were long enough that it the overhead really would be worth it, they would be surprising properties, indexers and constructors. As a consumer of someone's library, or as a maintainer of someone's code, it is outside my lexicon that these things will take significant time - they are things that are supposed to take a short amount of time.

This goes deeper than "just" needing to reeducate every C# programmer: the Visual Studio C# debugger has a very clear idea about what it thinks is supposed to be available in very short time (fields, properties, indexes insofar as they are aped via debugger display attributes) and what won't and may be side-effectful (iterators, methods). Additionally, object initializers combined with some of the new language features make sort of a perfect storm - constructors, indexer setters and property setters all running with high density in a single expression, together expected to take hardly any time at all.
May 7, 2014 at 9:28 PM
Many thanks for the detailed explanation and the ideas behind properties and indexers. To explain the thought behind async properties take this example:
    class foo<T> {
        T d;

        /// <summary>
        /// Synchronous Property
        /// </summary>
        public T Data {
            get { return d; }
            set { d = value; }
        }


        /// <summary>
        /// Asynchronous Getter of the Data property
        /// </summary>
        /// <returns>Data</returns>
        public async Task<T> GetData() {
            await Task.Delay(1000);
            return d;
        }

        /// <summary>
        /// Asynchronous Setter of the Data property
        /// </summary>
        /// <param name="data">Data</param>
        /// <returns>Task wich can be awaited</returns>
        public async Task SetData(T data) {
            await Task.Delay(1000);
            d = data;
        }
    }
The data property doesn't allow the awaiting of getter and setter. However, both can be formulated as async methods. The usage of the async GetData method would allow to wait for the finishing the getter. The usefulness of an async setter is not so obvious. However, there are cases in which you might want to wait for a successfully finished setter like during a transaction.
These features are found in the wild already like the 'IsAsync' property of WPF bindings and the AsyncLazy<T> in the Nito.AsyncEx nugget package.
Thus, I feel that the briefness of properties and indexes should be reconsidered in the C# language as async/await is more and more utilized.
May 8, 2014 at 9:12 PM
There are surely syntaxes to be found for declaring them. What's the syntax for using those properties? How would an example look like where you're "dotting into" three or four layers deep?
May 9, 2014 at 11:07 PM
Edited May 9, 2014 at 11:08 PM
A syntax proposal for the getter is:
    class bar {

        public bar foo {
            get { return this; }
        }

        public Task<bar> fooAsync {
            async get { return this; }
        }
    }

    class Program {
        static void Main(string[] args) {

            var b = new bar();

            // Monadic syntax
            var c = await b.foo.fooAsync.foo;

            // Sequential syntax
            var b1 = b.foo;
            var b2 = await b1.fooAsync;
            var c = b2.foo;
        }
    }
The 'async' keyword would precede the 'get' for getters returning Task<bar>. With this syntax the monadic dotting of properties would be possible because it can be expressed as sequential operation also. The 'async' would be 'infectious' when mixed with non-async properties which aligns with the current usage of async functions.
For read-only properties the getter should work well. For the setter of a property I run into difficulties. Firstly, the type of the setter is different from the getter. Instead of 'Task<bar>' I would like to set only 'bar'.
Secondly, tertiary assignment operations like 'i = j = 1' are defined in C# already. Thus, 'Task t = bar.fooAsync = b' is not possible, although this is necessary for returning a task from the setter function:
        public Task<bar> fooAsync {
            async set { _v = value; }
        }
However, 'async’ getters should syntactically possible and can co-exists with synchronous setters, until someone (maybe form the F# community…) come up with an elegant way of monads which pass one variable to the next monad and another variable back to the previous ;-)…
May 10, 2014 at 1:18 AM
You can really accomplish this today by having the property accessor method call a private async method:
public class Foo
{
    public Task<string> Name
    {
        get { return GetName(); }
    }

    private async Task<string> GetName()
    {
        await Task.Delay(1000);
        return "Hello!";
    }
}
That said I don't think that properties and async really fit well together. I can understand exposing a property of type Task<T> where the property represents some future potential value evaluated once to which multiple consumers could subscribe, but to have a property evaluate asynchronously for every invocation just seems counter to the purpose of a property.
May 15, 2014 at 7:45 PM
While async constructors that return a T make no sense, I wonder if there's a way to have a new form of constructor that returns Task<T>. Very often I find myself using factory methods for object construction because I need to do async calls in what would otherwise be the constructor body. For example,
class Foo
{
    private Data _data;

    private Foo() { }
   
    public static async Task<Foo> CreateAsync()
    {
        var foo = new Foo();
        foo._data = await GetDataAsync();
        return foo;
    }
}

var f = await Foo.CreateAsync();
This pattern is useful but involves a significant amount of boilerplate. It also is problematic in that you can't use readonly fields or the proposed get-only properties. Also, static methods don't play as nice with inheritance as normal constructors.

I don't have any concrete thoughts as to a good language solution for async constructors though. Thoughts?
May 15, 2014 at 8:59 PM
In my oppinion async constructors like
bar<T> b = await new bar<data>();
make sense because of the case you described.
However, beside your static factory pattern you could consider two other approaches.
The first one simply uses an async initialization method:
class foo<T> {
    private T _data;
    
    public foo() { }

    public async Task Initialize() {
        await Task.Delay(1000);
        _data = default(T);
    }

    public T Data {
        get { return _data; }
    }
}
This approach needs the call of Initialize() after every object construction which is highly prone to errors.
A second approach uses AsyncLazy with a non-static initialization function:
public class AsyncLazy<T> : Lazy<Task<T>> {
    public AsyncLazy(Func<T> valueFactory) : base(() => Task.Factory.StartNew(valueFactory)) { }
    public AsyncLazy(Func<Task<T>> taskFactory) : base(() => Task.Factory.StartNew(() => taskFactory()).Unwrap()) { }
}

class bar<T> {
    private AsyncLazy<T> _data;

    private async Task<T> Initialize() {
        await Task.Delay(1000);
        return default(T);
    }

    public bar() {
        _data = new AsyncLazy<T>(() => this.Initialize());
    }

    public async Task<T> Data() {
        return await _data.Value;
    }
}
You can use the AsyncLazy approach without additionally function calls:
bar<int> b = new bar<int>();
int data = await b.Data();
However, the drawback of the second soluiton is that the property getter has to be implemented as function as async getters don’t exist up to now.
May 16, 2014 at 9:25 AM
@MgSam Take a look at this MSDN article. It contains some useful async patterns regarding async creation and initialization
May 16, 2014 at 3:01 PM
@Przemyslaw Thanks. That was interesting, though none of those patterns really solve the core problem. Forcing the caller to call a separate Initialize() method (or property) is exactly the situation I want to avoid. That's why I use the factory pattern I described.

I think there must be some language solution for constructors to enable async operations more directly.

The obvious syntax would seem to be this:
class Foo
{
    public async Foo()
    {
         await GetDataAsync();
     }
}

var a = await new Foo();
Where decorating the constructor with async makes it return a Task<T> rather than a T.

From a pure syntax perspective I think this works, but I don't know IL so I don't know what kinds of problems this might cause there.
May 16, 2014 at 9:25 PM
MgSam wrote:
From a pure syntax perspective I think this works, but I don't know IL so I don't know what kinds of problems this might cause there.
I think the biggest problem will be assigning readonly fields. You have to be able to invoke each part of your async constructor between two awaits independently, so they can't all be in the IL constructor (.ctor). But all parts should be able to assign readonly fields, so they can't be in a normal IL method either.