让我们假设我有一个长期运行的Web API调用(async方法),该调用返回字符串。
这两个解决方案之间是否有最佳实践在不阻止UI的情况下显示结果?还是还有另一个?
注意:两种解决方案都不冻结UI,我已经查看了如何从Getter或Setter调用异步方法的帖子?和c#中的异步属性。
我的WEP API
private async Task<string> GetAsyncProperty()
{
string result = "Async Property Value";
// Web api call...
await Task.Delay(TimeSpan.FromSeconds(10));
return result;
}
解决方案
xaml:
<TextBlock Text="{Binding Path=AsyncPropertyA, UpdateSourceTrigger=PropertyChanged}" />
ViewModel:
public MyConstructor()
{
Task task = SetAsyncPropertyA();
}
private async Task SetAsyncPropertyA()
{
this.AsyncPropertyA = await GetAsyncProperty().ConfigureAwait(false);
}
解决方案B
xaml:
<TextBlock Text="{Binding Path=AsyncPropertyB, UpdateSourceTrigger=PropertyChanged, IsAsync=True, FallbackValue='Loading B...'}" />
ViewModel:
public string AsyncPropertyB
{
get
{
return GetAsyncPropertyB();
}
}
private string GetAsyncPropertyB()
{
return Task.Run(() => GetAsyncProperty()).Result;
}
注意:在解决方案B中,我可以添加在解决方案A中不起作用的后备值,并且可能在任务继续进行的其他UI更新。
在两种情况下,您都不会遇到任何尝试调用Web API时可能发生的错误。您可能需要将其记录到文件和/或向用户显示错误消息。
在这种情况下,等待变得容易 - 您可以使用try/catch:
public MyConstructor()
{
try
{
Task task = SetAsyncPropertyA();
}
catch (Exception e)
{
// log the error or show a message
}
}
private async Task SetAsyncPropertyA()
{
this.AsyncPropertyA = await GetAsyncProperty().ConfigureAwait(false);
}
您也可以将尝试/捕获移动到异步方法。在这种情况下,由于没有错误逃脱的机会,因此您可以使其异步无效。有时,这对于事件处理程序(至少在Windows表单中 - 不确定WPF)是必需的。)
public MyConstructor()
{
SetAsyncPropertyA();
}
private async void SetAsyncPropertyA()
{
try
{
this.AsyncPropertyA = await GetAsyncProperty().ConfigureAwait(false);
}
catch (Exception e)
{
// log the error or show a message
}
}
您应该尝试为此使用一个好的框架,该框架已经开启了。
查看reactiveui命令示例:
LoadTweetsCommand = ReactiveCommand.CreateAsyncTask(() => LoadTweets())
LoadTweetsCommand.ToProperty(this, x => x.TheTweets, out theTweets);
LoadTweetsCommand.ThrownExceptions.Subscribe(ex => /* handle exception here */);
这些扩展程序在iObservable上起作用,这本身就是非常强大的工具:
Observable.FromAsync(async () =>
{
await Task.Delay(100);
return 5;
}).ToProperty(x => )