Please forgive me for any noobish mistakes seen below, I'm learning some of the concepts I'm attempting to work with.
Problem:
While debugging my app, I was able to call an async
function with Task.Start()
. I felt that the app was in a working state for the phase I'm in so removed all breakpoints with CTRL + SHIFT + F9.
Once I ran the app with no breakpoints it would fail due to a property not getting populated. Now when I try to debug any breakpoint I set in the async
function that handles most of the work is longer hit. It's like it is getting skipped. Can anyone see a reason why GetWowAuctionFileInfo
isn't being called?
GetWowAuctionFileInfo
is what is not getting called, or at least appears to be not getting called.
Thanks.
Relevant Code
Caller Function
private void buttonTestJSFCHI_Click(object sender, RoutedEventArgs e)
{
JSON_Worker w = new JSON_Worker();
w.StartTask("FileInfo", "https://us.api.battle.net/wow/auction/data/medivh?locale=en_US&apikey=<guid>");
foreach (string res in w.ReturnedData)
{
textBoxResults.Text += res;
}
}
Called Functions
public void StartTask(string TaskName, string optionalUri= "no_uri_passed")
{
if (TaskName == "FileInfo")
{
//Need to use a lamba expression to call a delegate with a parameter
if (!(optionalUri == "no_uri_passed"))
{
Task t = new Task(() => GetWowAuctionFileInfo(optionalUri));
t.Start();
//Func<string> function = new Func<string>(() => GetWowAuctionFileInfo(optionalUri));
//Task<string> tInfo = Task<string>.Factory.StartNew(() => GetWowAuctionFileInfo(optionalUri));
}
}
}
private async void GetWowAuctionFileInfo(string auctionInfoUri)
{
RealmJSFileCheck realmInfoObject;
List<string> returnValue = new List<string>();
try
{
using (HttpClient client = new HttpClient())
{
for (int attempt = 0; attempt < 3; attempt++)
{
var response = await client.GetAsync(auctionInfoUri);
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
realmInfoObject = JsonConvert.DeserializeObject<RealmJSFileCheck>(content);
returnValue = ConvertFileInfoToConsumableList(realmInfoObject);
//returnValue = realmInfoObject.files.ToString();
break;
}
}
}
}
catch (InvalidOperationException iOpEx)
{
//recieved this when an invalid uri was passed in
}
ReturnedData = returnValue;
}
private List<string> ConvertFileInfoToConsumableList(RealmJSFileCheck jsfc)
{
List<string> returnData = new List<string>();
if (jsfc.files.Count > 0)
{
StringBuilder sb = new StringBuilder();
sb.Append("File URL: ");
sb.Append(jsfc.files[0].url);
returnData.Add(sb.ToString());
sb = new StringBuilder();
sb.AppendLine("Last Modified: ");
sb.Append(jsfc.files[0].lastModified);
returnData.Add(sb.ToString());
}
else
{
returnData.Add("No File Info Found");
}
return returnData;
}
UPDATE Thanks again all for the detailed commentary. I've gone through much documentation regarding Task usage and learned a lot in this exercise. I'm marking the answer from @Johnathon as the solution because it provided exactly what I was asking for and provided a very helpful link for more information.
答案 0 :(得分:3)
您的GetWowAuctionFileInfo
方法是一种异步方法,您可以在其中等待异步调用而不返回任务。一般来说,使用async void
是不好的做法。而是将GetWowAuctionFileInfo
方法转换为async Task<List<string>> GetWowAuctionFileInfo
。这将让您等待GetAsync
调用,解析数据,并将集合返回给调用者,而无需使用ReturnObject
。
private async Task<List<string>> GetWowAuctionFileInfo(string auctionInfoUri)
{
RealmJSFileCheck realmInfoObject;
List<string> returnValue = new List<string>();
try
{
using (HttpClient client = new HttpClient())
{
for (int attempt = 0; attempt < 3; attempt++)
{
var response = await client.GetAsync(auctionInfoUri);
if (response.IsSuccessStatusCode)
{
string content = await response.Content.ReadAsStringAsync();
realmInfoObject = JsonConvert.DeserializeObject<RealmJSFileCheck>(content);
// You can just return the List<T> now.
return ConvertFileInfoToConsumableList(realmInfoObject);
//returnValue = realmInfoObject.files.ToString();
break;
}
}
}
}
catch (InvalidOperationException iOpEx)
{
//recieved this when an invalid uri was passed in
}
}
由于该方法原来是async void
,因此无法等待buttonTestJSFCHI_Click
中的方法调用。现在我们已经完成了基于任务的任务,您可以在事件处理程序中等待它。请注意,事件处理程序通常是唯一可以使用async void
的地方。任何时候你负责创建方法,而不受合同约束(比如事件处理程序),你应该总是在异步方法上返回一个Task。
private async void buttonTestJSFCHI_Click(object sender, RoutedEventArgs e)
{
JSON_Worker w = new JSON_Worker();
List<string> results = await w.StartTask("FileInfo", "https://us.api.battle.net/wow/auction/data/medivh?locale=en_US&apikey=<guid>");
foreach (string res in results)
{
textBoxResults.Text += res;
}
}
public async Task<List<string>> StartTask(string TaskName, string optionalUri= "no_uri_passed")
{
if (TaskName == "FileInfo")
{
//Need to use a lamba expression to call a delegate with a parameter
if (!(optionalUri == "no_uri_passed"))
{
// Since the GetWowAuctionFileInfo now returns Task, we don't need to create a new one. Just await the Task given back to us, and return the given result.
return await GetWowAuctionFileInfo(optionalUri);
}
}
}
您在调试时看到预期结果的原因是因为调试会话足够慢以至于异步操作及时完成,您的代码才能使用它。在调试器外部运行应用程序时,它的运行速度比异步操作可以完成的速度快,从而阻止您查看数据。因此需要await
整个异步调用堆栈,这样就可以防止在该代码路径下发生进一步的执行,直到您收到所有需要的数据。
Microsoft has a good write up关于基于任务的编程,我会仔细阅读它以帮助您理解它。
修改的
只是澄清一下,当您在方法上返回Task<T>
时,您将在等待时获得结果。例如:
List<string> result = await StartTask();
即使StartTask
返回Task<List<string>>
,await
操作也会等待StartTask()
方法完成,然后从Task<T>
对象中解包结果并自动返回结果。所以不要让方法签名欺骗你,如果你await
,你将得到结果数据,而不是实际的Task
本身。您无需手动将数据从Task
中提取出来。
答案 1 :(得分:1)
因为你不等待结果
在为数据分配之前,您使用ReturnedData
循环。
我认为您根本不需要创建新的Task
。使GetWowAuctionFileInfo
方法正确异步,返回Task
。
private async Task GetWowAuctionFileInfo(string auctionInfoUri)
{
// same code
}
将StartTask
更改为返回Task
。因为我们不在这里等待结果,所以我们不需要make方法异步
建议将此方法的名称更改为LoadData
,例如,它提供有关此方法的更多信息。
public Task StartTask(string TaskName, string optionalUri= "no_uri_passed")
{
if (TaskName == "FileInfo")
{
//Need to use a lamba expression to call a delegate with a parameter
if (!(optionalUri == "no_uri_passed"))
{
return GetWowAuctionFileInfo(optionalUri) // this will return Task
}
}
// if validation fails - return completed task or throw exception
return Task.CompletedTask;
}
然后你可以在Button_Click
事件处理程序
private async void buttonTestJSFCHI_Click(object sender, RoutedEventArgs e)
{
JSON_Worker w = new JSON_Worker();
await w.StartTask("FileInfo", "yourUrl");
// This line will be executed only after asynchronous methods completes succesfully
// or exception will be thrown
foreach (string res in w.ReturnedData)
{
textBoxResults.Text += res;
}
}