Async Function Not Getting Called

时间:2016-11-26 18:39:38

标签: c# json .net-4.5

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.

2 个答案:

答案 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;
    }
}