2

I am using winforms to scrape JSON data from a website.

The problem is the method doesn't complete and code execution halts at the line // HttpResponseMessage response = await client.PostAsync($"https://ticks.site/api/numberofticks/geticks?ticks={strTicks}", headers);.

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TicksProgram
{
    public partial class Scanner : Form
    {
        public Scanner()
        {
            InitializeComponent();

            GetJSON().GetAwaiter().GetResult();
        }

        private async Task GetJSON()
        {
            var client = new HttpClient();

            // headers
            var headers = new FormUrlEncodedContent(new[] {
                new KeyValuePair<string, string>("somestring", "somestring"),
               });


            string strTicks = "5";
            // Get the response.
            HttpResponseMessage response = await client.PostAsync(
                $"https://ticks.site/api/numberofticks/geticks?ticks={strTicks}", headers);
            
            // Get the response content.
            HttpContent responseContent = response.Content;
        }
    }
}

There are no errors.

I am new to async so I am unsure what to do.

  • Everywhere that should be awaited is awaited. Which line is causing that message? – ProgrammingLlama Mar 10 '21 at 04:58
  • Do a rebuild of your solution and see if the warning is still there. Removing warnings is a low priority task for visual studio, if you are running on a slow machine those warnings can stay there for quite a while before VS gets around to rechecking them. – The Lemon Mar 10 '21 at 05:03
  • 1
    The warning isn't from this code. It's somewhere else. You need to copy and paste the entire warning: CS file and line number. – Andy Mar 10 '21 at 05:03
  • Correct, the line which the warning complains about is line 22, which is where I am calling the this method in my MAIN. Although awaiting GetJSON() results in this error ```Severity Code Description Project File Line Suppression State Error CS4033 The 'await' operator can only be used within an async method. Consider marking this method with the 'async' modifier and changing its return type to 'Task'.``` The return type is indeed Task as you can see, the former part of the message is what let me to only post the body of the method because it's all that this says is relevant. – alex_1995_henry Mar 10 '21 at 05:16
  • 1
    in `Main()`, change the call from `GetJSON();` to `GetJSON().GetAwaiter().GetResult();` Or, change the signature from `void Main()` to `async Task Main()` and call `await GetJSON();` – Andy Mar 10 '21 at 05:18
  • @Andy instead of skipping the code execution at that point, now it pauses at that point, I guess there is a problem with my web request and the await is never filled and thats why it is halting. No errors so, got any other ideas? Also something to note is it shouldn't be any issue with my web request as I have completed this exact code within python and it runs. – alex_1995_henry Mar 10 '21 at 05:22
  • Are you `await`ing the call within async `Main`? If so, there would be no error, otherwise you have to block on this call `GetJSON().Wait()`. – Alsein Mar 10 '21 at 05:22
  • I said MAIN before to make it simple to understand where I am running this from, but in actuality I am using windows forms in Visual studio so there isn't main and I don't believe I have permission to change it from its default setting. – alex_1995_henry Mar 10 '21 at 05:24
  • you need to post all the details of your problem. Everything you put in your question has nothing to do with your problem. Every comment you make you add a little more that should have been in the question. – Andy Mar 10 '21 at 05:25
  • Please provide a [mcve]. As it stands we can't reproduce your problem, and we don't know how you're calling this method (by your own admission, the calling code is the problem), so we can't advise how to fix it. – ProgrammingLlama Mar 10 '21 at 05:30
  • 2
    You say you're using `GetJSON().GetAwaiter().GetResult();` and that execution halts. That is probably a deadlock. When the IO-bound operation (the web request) releases the thread, it saves the current context. When the IO-bound operation completes, it tries to resume this context (in this case, resume the operation on the same thread). To do so, it waits for the thread to become available. So what we have happening is: `.GetAwaiter().GetResult()` is waiting for the async operation to complete, and the async operation is waiting for the thread to be free i.e. waiting for that line to complete. – ProgrammingLlama Mar 10 '21 at 05:33
  • 1
    The correct solution is likely to make everything up the chain async, and to use the async/await pattern. – ProgrammingLlama Mar 10 '21 at 05:34
  • As a side note, *HttpClient is intended to be instantiated once and re-used throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads.* ([citation](https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient#remarks)) – Theodor Zoulias Mar 10 '21 at 05:36
  • @John Okay everything is in order. – alex_1995_henry Mar 10 '21 at 05:42
  • @John ah I see so its a circular loop of dependency. I'll need to chain the dependencies, I guess I should read the docs more or just drop async and find another method, learning multi-threading is tough. – alex_1995_henry Mar 10 '21 at 05:43
  • I've added an answer to your question for your specific scenario, but it's roughly the same problem as in [this question](https://stackoverflow.com/questions/25456194/winforms-call-to-async-method-hangs-up-program). – ProgrammingLlama Mar 10 '21 at 05:46
  • 3
    Check out this article: [Don't Block on Async Code](https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html). It's very informative regarding problems of this nature. – Theodor Zoulias Mar 10 '21 at 05:46
  • 1
    @TheodorZoulias +1 for anything by Stephen on async/await. – ProgrammingLlama Mar 10 '21 at 05:47
  • @TheodorZoulias Reading now, this is probably the issue, but I just have to get my head around it. – alex_1995_henry Mar 10 '21 at 08:02
  • alex_1995_henry this is probably the most frequent problem that the newcomers to async/await have to deal with. It's not a dangerous problem, because it is unlikely to slip unnoticed to the production code, but it can be quite frustrating during the developement. After having dealt with it, you are going to see every occurrence of `.Wait()`, `.Result` and `.GetAwaiter().GetResult()` in your code as minefields that need to be eradicated with care. :-) – Theodor Zoulias Mar 10 '21 at 08:12

1 Answers1

1

You should move your code to the Form_Load event (you can add this via the Windows Forms Designer UI):

private async void Form_Load(object sender, EventArgs e)
{
    await GetJSON();
}

In 99/100 cases, you should not use async void and you should use async Task instead. In the case of WinForms event handlers, you have no other option (you can't change the method signature) and async void is the recommended way to do this.

ProgrammingLlama
  • 36,677
  • 7
  • 67
  • 86