我正忙着使用当然使用silverlight的Windows手机应用程序。这意味着调用任何web服务都必须异步完成,因为这对于防止整个应用程序在等待资源时挂起的最佳实践都很好,我仍然坚持“同步思维模式”。 ..
因为我现在看到的方式是你最终有两种方法需要处理一个功能,例如:
1)实际调用webservice的方法:
public void myAsyncWebService(DownloadStringCompletedEventHandler callback)
{
//Url to webservice
string servletUrl = "https://deangrobler.com/someService/etc/etc"
//Calls Servlet
WebClient client = new WebClient();
client.DownloadStringCompleted += callback;
client.DownloadStringAsync(new Uri(servletUrl, UriKind.Absolute));
}
2)以及最终返回时处理数据的方法:
private void serviceReturn(object sender, DownloadStringCompletedEventArgs e)
{
var jsonResponse = e.Result;
//and so on and so forth...
}
因此,不必仅创建并调用转发到webservice的单个方法,而是获取返回的结果并将其发送回给我:
public string mySyncWebService(){
//Calls the webservice
// ...waits for return
//And returns result
}
我必须在类中调用myAsyncWebService, AND 创建调用类中的另一个方法,它将处理myAsyncWebService返回的结果。在我看来,只是创建了凌乱的代码。使用同步调用,您只需调用一个方法即可完成。
我刚刚使用异步调用错了吗?我的理解错了吗?我需要一些启发,我讨厌做这个凌乱的异步调用。它使我的代码太复杂,可读性只是去......地狱。
感谢任何愿意改变主意的人!
答案 0 :(得分:2)
你必须把你的想法转变为异步编程。我是根据经验说的。 :)
我刚刚使用异步调用错了吗?我的理解错了吗?
没有。异步代码很难编写(不要忘记错误处理)并且极难维护。
这就是async
和await
被发明的原因。
如果您能够升级到VS2012,那么您可以使用Microsoft.Bcl.Async(目前处于测试阶段)编写如下代码:
string url1 = "https://deangrobler.com/someService/etc/etc";
string jsonResponse1 = await new WebClient().DownloadStringTaskAsync(url1);
string url2 = GetUriFromJson(jsonResponse1);
string jsonResponse2 = await new WebClient().DownloadStringTaskAsync(url2);
易于书写。易于维护。
答案 1 :(得分:1)
异步就像打电话和接听电话一样,如果你想要一个回电话就可以留下号码。第一种方法是要求提供数据的电话,第二种方法是您为返回电话留下的“号码”。
答案 2 :(得分:1)
使用同步调用,您只需调用一个方法即可完成。
当然,但是如果你从UI线程那样做,那么你将阻止整个UI。这在任何现代应用程序中都是不可接受的,特别是在浏览器或手机中运行的Silverlight应用程序中。在DNS查找超时的情况下30秒没有响应的电话不是任何人想要使用的。
因此在UI线程上,可能是因为用户在UI中执行了某些操作,您启动了异步调用。当调用完成时,在后台线程上调用一个方法来处理调用的结果。此方法很可能会使用异步调用的结果更新UI。
随着在.NET 4.5中引入async和await,可以简化一些“拆分”代码。幸运的是,async和await现在可用于使用NuGet包Microsoft.Bcl.Async的测试版中的Windows Phone 7.5。
这是一个小的(有点愚蠢的)示例,演示了如何使用异步链接两个Web服务调用。这适用于.NET 4.5,但使用上面链接的NuGet包,您应该可以在Windows Phone 7.5上执行类似的操作。
async Task<String> GetCurrencyCode() {
using (var webClient = new WebClient()) {
var xml = await webClient.DownloadStringTaskAsync("http://freegeoip.net/xml/");
var xElement = XElement.Parse(xml);
var countryName = (String) xElement.Element("CountryName");
return await GetCurrencyCodeForCountry(countryName);
}
}
async Task<String> GetCurrencyCodeForCountry(String countryName) {
using (var webClient = new WebClient()) {
var outerXml = await webClient.DownloadStringTaskAsync("http://www.webservicex.net/country.asmx/GetCurrencyByCountry?CountryName=" + countryName);
var outerXElement = XElement.Parse(outerXml);
var innerXml = (String) outerXElement;
var innerXElement = XElement.Parse(innerXml);
var currencyCode = (String) innerXElement.Element("Table").Element("CurrencyCode");
return currencyCode;
}
}
但是,您仍需要在UI线程和异步GetCurrencyCode
之间架起桥梁。您无法在事件处理程序中等待,但可以对异步调用返回的任务使用Task.ContinueWith
:
void OnUserAction() {
GetCurrencyCode().ContinueWith(GetCurrencyCodeCallback);
}
void GetCurrencyCodeCallback(Task<String> task) {
if (!task.IsFaulted)
Console.WriteLine(task.Result);
else
Console.WriteLine(task.Exception);
}
答案 3 :(得分:1)
如果你使用lambdas,这一切都变得更容易和可读。这也使您能够访问在&#34; parent&#34;中声明的变量。方法,如下例所示:
private void CallWebService()
{
//Defined outside the callback
var someFlag = true;
var client = new WebClient();
client.DownloadStringCompleted += (s, e) =>
{
//Using lambdas, we can access variables defined outside the callback
if (someFlag)
{
//Do stuff with the result.
}
};
client.DownloadStringAsync(new Uri("http://www.microsoft.com/"));
}
编辑:这是两个链式服务调用的另一个例子。它仍然不是很漂亮,但是它比OP原始代码更具可读性。
private void CallTwoWebServices()
{
var client = new WebClient();
client.DownloadStringCompleted += (s, e) =>
{
//1st call completed. Now make 2nd call.
var client2 = new WebClient();
client2.DownloadStringCompleted += (s2, e2) =>
{
//Both calls completed.
};
client2.DownloadStringAsync(new Uri("http://www.google.com/"));
};
client.DownloadStringAsync(new Uri("http://www.microsoft.com/"));
}
答案 4 :(得分:1)
为了避免创建凌乱的代码,如果您不能使用 async / await 模式,因为您使用的是较旧的框架,则会在CoRoutines中找到有用的检查Caliburn Micr实施。使用这种模式,你可以在每个回合中创建一个可枚举的一个新的异步段来执行:从读者的角度看,异步步骤显示为一个序列,但是在步骤之间行走(因此产生下一个步骤)是通过异步等待外部完成的。单一任务。这是一个很容易实现的好模式,读起来非常清晰。 顺便说一句,如果您不想使用Caliburn Micro作为MVVM工具,因为您正在使用其他东西,您可以只使用协同设施,它在框架内非常绝缘。
让我发布一些this blog post中的示例代码。
public IEnumerable<IResult> Login(string username, string password)
{
_credential.Username = username;
_credential.Password = password;
var result = new Result();
var request = new GetUserSettings(username);
yield return new ProcessQuery(request, result, "Logging In...");
if (result.HasErrors)
{
yield return new ShowMessageBox("The username or password provided is incorrect.", "Access Denied");
yield break;
}
var response = result.GetResponse(request);
if(response.Permissions == null || response.Permissions.Count < 1)
{
yield return new ShowMessageBox("You do not have permission to access the dashboard.", "Access Denied");
yield break;
}
_context.Permissions = response.Permissions;
yield return new OpenWith<IShell, IDashboard>();
}
阅读不容易吗?但它实际上是异步的:每个yield
步骤都以异步方式执行,并且在上一个任务完成后,yield
语句后执行流程再次流动。