将其归结为您实际要求的是能够对执行异步任务但仅在异步任务完成时返回的 API 进行同步调用。换一种说法,您希望将异步操作的开始和结束阶段重新组合回单个原子同步操作。
实现这一点的唯一方法是阻塞进行调用的线程,直到达到异步操作的结束阶段。出于多种原因,这不是一个好主意。
如果您认真使用 Silverlight,您必须接受它的异步特性并在该框架中使用,而不是试图将其强制返回到同步系统中。
将同步转换为异步
阅读blog,了解如何将同步代码转换为异步代码。现在让我们想象一下您的代码在名为DownloadYourDataObjects 的新DLL 中使用了一个同步方法,该方法具有这个虚构的签名:-
public IEnumerable<YourDataObject> DownloadYourDataObjects(Uri source);
在内部,它使用 WebClient 从 REST 基础服务下载字符串并将其转换为一组 YourDataObject 实例。显示这组数据的假想同步代码可能是:-
private void btnLoadMyData_Click(object sender, RoutedEventArgs e)
{
try
{
LoadMyData();
}
catch (Exception err)
{
// Oops something bad happened show err.
}
}
private void LoadMyData()
{
DataItemsListBox.ItemsSource = DownloadYourDataObjects(someUri);
}
由于 Silverlight WebClient 是异步的,我们需要将整个代码链转换为以异步方式工作。
使用博客中的AsyncOperationService,我们首先需要将DownloadYourDataObjects 转换为返回AsyncOperation。它会有这样的签名(见后面的实现思路):-
public AsyncOperation DownloadYourDataObjects(Uri source, Action<IEnumerable<YourDataObject>> returnResult);
然后使用代码将如下所示:-
private void btnLoadMyData_Click(object sender, RoutedEventArgs e)
{
LoadMyData().Run(err =>
{
if (err != null)
{
// Oops something bad happened show err.
}
});
}
private IEnumerable<AsyncOperation> LoadMyData()
{
yield return DownloadYourDataObjects(someUri, result =>
{
DataItemsListBox.ItemsSource = result;
});
}
这可能看起来有点 OTT,但实际上它的代码并不比原始的“同步”版本多多少。在这个简单的例子中,LoadMyData 只需要执行一个操作。 LoadMyData 的更复杂版本可能还有多个其他需要异步的操作。在这种情况下,这些操作将只是代码中的其他 yield 点,LoadMyData 的基本逻辑结构与原始同步版本相比不会有太大变化。
下面是您的 DLL 将提供的DownloadYourDataObjects 实现示例。
public AsyncOperation DownloadYourDataObjects(Uri source, Action<IEnumerable<YourDataObject>> returnResult)
{
return (completed) =>
{
WebClient client = new WebClient();
client.DownloadStringCompleted += (s, args) =>
{
try
{
returnResult(ConvertStringToYourDataObjects(args.Result));
completed(null);
}
catch (Exception err)
{
completed(err);
}
};
client.DownloadStringAsync(source);
};
}