Friday, May 30, 2014

Awaitable functions in properties

If you've ever needed to call an asynchronous function from within a property setter, you may have been confused, or at least unhappy with the result.  You know that it needs the "await" operator, but if you add it you'll get an error since it's not even possible to make a property async.

public string Name
{
    get{ return _name; }
    set{ await VerifyAsync(value); RaisePropertyChanged("Name"); }
}

If you don't add await, then you'll get a compiler warning, but you'll also lose the flow that you have.  This will probably result in a notify change event being called before your code finishes running.  This is because if you don't await, the asynchronous call is dispatched to a background worker and your code continues running.  This means that interested parties get events before the value might be truly updated!

public string Name
{
    get{ return _name; }
    set{ VerifyAsync(value); RaisePropertyChanged("Name"); }
}

You might be tempted to wait on it, but in many cases this causes issues with deadlocks, or causes responsiveness issues that the async pattern is trying to fix.

public string Name
{
    get{ return _name; }
    set{ VerifyAsync(value).Wait(); RaisePropertyChanged("Name"); }
}

It turns out that the fix is pretty simple by using a delegate and the Task.ContinueWith method.  This lets you dictate code to run when an async call finishes, without using the await keyword.  Just add your code that needs to run after and you'll know it will be in the right order!

public string Name
{
    get{ return _name; }
    set{
        VerifyAsync(value).ContinueWith((t)=> {
            RaisePropertyChanged("Name"); }
    }
}

No comments:

Post a Comment