Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use await/async with synchronous code?

I'm trying to use await/async in order to make some synchronous code asynchronous. For example, this works and unblocks the UI thread:

private async void button1_Click(object sender, EventArgs e)
{
    var task = DoRequestAsync();
    textBox1.Text = "starting async task";
    var text = await task;
    textBox1.Text = text;
}

private async Task<string> DoRequestAsync()
{
    try
    {
        var client = new HttpClient();
        client.Timeout = new TimeSpan(0, 0, 0, 5);
        await client.GetAsync("http://123.123.123.123"); // force a timeout exception
    }
    catch (Exception e)
    {
    }

    return "done!";
}

But this doesn't, and will hang the UI thread:

private async void button1_Click(object sender, EventArgs e)
{
    var task = DoRequestAsync();
    textBox1.Text = "starting async task";
    var text = await task;
    textBox1.Text = text;
}

private async Task<string> DoRequestAsync()
{
    try
    {
        var request = WebRequest.Create("http://123.123.123.123");
        request.Timeout = 5000;
        request.GetResponse(); // force a timeout exception
    }
    catch (Exception e)
    {
    }

    return "done!";
}

I'm trying to understand why this is the case. I was under the impression that var task = DoRequestAsync() will create a new thread and run everything in the method asynchronously, but that appears to not be the case.

I can use this to make it work:

await Task.Run(() => {
    var request = WebRequest.Create("http://123.123.123.123");
    request.Timeout = 5000;
    request.GetResponse();
});

But this seems a bit hacky and I'm not sure if it's the right solution to this issue. Does anybody know how I can just run a bunch of synchronous code in an asynchronous way using Tasks and async/await?

like image 544
Daniel T. Avatar asked Aug 06 '14 04:08

Daniel T.


1 Answers

That's the right solution. WebRequest.GetResponse is not an async method therefore it does not return a Task. It cannot run asynchronously.

Actually, what you have there is the most correct and shorthand solution you can get (with Task.Run).

I was under the impression that var task = DoRequestAsync() will create a new thread and run everything in the method asynchronously, but that appears to not be the case.

It's not magic. In order for it to run asynchronously, a new Task (not thread) must be created in your async method or it must await on one or more methods)that return either Task, Task<T> or void (this is for event handlers).

Your last statement in the method return "done!"; just returns a completed Task<string> with result "done".

As a side note, this is why HttpClient is becoming the de facto class HTTP requests, especially for interoperating with web APIs but also for general purpose GETs/POSTs/etc: it has async support.

Tasks also have support to wrap Begin*/End* functions (conforming to the former asynchronous programming model - APM). You can also do:

try
{
    var request = WebRequest.Create("http://123.123.123.123");
    request.Timeout = 5000;
    await Task.Factory.FromAsync(request.BeginGetResponse(), request.EndGetResponse, null); // force a timeout exception
}
catch (Exception e)
{
    //TODO handle exception here
}
like image 92
Marcel N. Avatar answered Oct 03 '22 05:10

Marcel N.