WebClient GET/POST without blocking UI - c#

I would like to make non blocking GET and POST requests. I've managed to solve it with BackgroundWorker, but I need your help to achieve it using tasks.
public Task<string> Post(Uri uri, string data)
{
return _webClient.UploadStringTaskAsync(uri, data);
}
public Task<string> Get(Uri uri)
{
return _webClient.DownloadStringTaskAsync(uri);
}
I need the requests to run sequentially. What's the proper way to implement this? Flag methods with async and await them? Wait for each task using Task.WaitAll()?
An example of what I'm after:
Task<string> loginTask = Post("login", data);
// wait for webrequest to complete and make use of response string
// use data from first request in a new request:
Task<string> someOtherRequest = Get("details");

You can use await Task.Run():
await Task.Run(()=> { Run_UI_Blocking_Function(); });
For example:
In the Post() function, you can add this:
await Task.Run(()=> { PostString = Post(new Uri("url"), "data"); });
And in the 'Get()' function, you can add this:
await Task.Run(()=> { GetString = Get(new Uri("url")); });
You'd just need to add async to your calling function. Here's a completed example:
private async void btn_PostData_Click(object sender, EventArgs e)
{
await Task.Run(()=> { PostString = Post(new Uri("url"), "data"); });
}
Hope this helps. It's really short and sweet.

You need to mark the method as async, and await the result.
public async Task<string> Get(Uri uri)
{
return await _webClient.DownloadStringTaskAsync(uri);
}
You can also install the Microsoft HttpClient library, which is already plumbed for async calls and may be a little lighter weight.

Related

Defer starting of a Task<T>

I have a series of methods (with variable number of parameters) that return a Task
I want to create a method that does something before and after each of this methods, by passing the Task.
I've simplified everything (removed cancellationToken, actual processing, etc) in this sample:
public async Task<string> GetDataString()
{
Console.WriteLine("Executing");
return "test";
}
public async Task<T> Process<T>(Task<T> task)
{
Console.WriteLine("Before");
var res = await task;
Console.WriteLine("After");
return res;
}
And in my main:
Task<string> task = GetDataString();
string result = await Process<string>(tasks);
Console.WriteLine(res);
the console output is
Executing
Before
After
test
What can I do to create the task but not actually starting it? And starting it only before the wait?
I managed to do it by creating a PauseToken, as explained in this article:
https://devblogs.microsoft.com/pfxteam/cooperatively-pausing-async-methods/
but I wonder if is there a better way.
Thanks,
Mattia
Your generic ProcessAsync method could accept a task factory as argument:
public async Task<T> ProcessAsync<T>(Func<Task<T>> taskFactory)
{
Console.WriteLine("Before");
var res = await taskFactory();
Console.WriteLine("After");
return res;
}
This way the task will be created at the time you'll invoke the factory method. You are in control of its creation.
Here is an example of calling the ProcessAsync method, passing as factory a lambda:
var result = await ProcessAsync(() => GetDataStringAsync(arg1, arg2));
This way you are not restricted to a factory method without arguments.
For completeness I should mention that Task objects can also created in a cold state using the constructor new Task(), and started later using the Start method, but this approach is not recommended.
You can remove the async keyword (from GetDataString) and create a new task which will be executed when you await
so the result of the code below is : before , executing , test , after
private static async Task Start()
{
Task<string> task = GetDataString();
string result = await Process<string>(task);
Console.WriteLine(result);
Console.ReadLine();
}
public Task<string> GetDataString()
{
return new TaskFactory(TaskScheduler.Default).StartNew(() =>
{
Console.WriteLine("Executing");
return "test";
});
}
public async Task<T> Process<T>(Task<T> task)
{
Console.WriteLine("Before");
var res = await task;
Console.WriteLine("After");
return res;
}

Async call inside synchronous webservice

I have a web service which calls an external web service. The external service responds after 3-4 seconds.
Right now all the calls are synchronous, but does it make sense to use async calls instead (sample below)?
Will it help with performance (not keeping threads blocked)? Isn't the thread blocked on the first line of GetData()?
Thank you.
public class MyService : WebService
{
[WebMethod]
public string GetData()
{
string response = ExecuteRequest(externalUrl, someContent).Result;
return response;
}
private async Task<string> ExecuteRequest(string url, string content)
{
var httpResponse = await new HttpClient().PostAsync(url, new StringContent(content));
string responseStr = await httpResponse.Content.ReadAsStringAsync();
return responseStr;
}
}
To answer your question: Yes, it does make sense to use async calls instead, but your example is not async. If you wanted to make it async you'd have to do something like this:
public class MyService : WebService
{
[WebMethod]
public async Task<string> GetData()
{
string response = await ExecuteRequest(externalUrl, someContent);
return response;
}
private async Task<string> ExecuteRequest(string url, string content)
{
var httpResponse = await new HttpClient().PostAsync(url, new StringContent(content));
string responseStr = await httpResponse.Content.ReadAsStringAsync();
return responseStr;
}
}

async method callback with Task.ContinueWIth?

I have a method that pulls some HTML via the HttpClient like so:
public static HttpClient web = new HttpClient();
public static async Task<string> GetHTMLDataAsync(string url)
{
string responseBodyAsText = "";
try
{
HttpResponseMessage response = await web.GetAsync(url);
response.EnsureSuccessStatusCode();
responseBodyAsText = await response.Content.ReadAsStringAsync();
}
catch (Exception e)
{
// Error handling
}
return responseBodyAsText;
}
I have another method that looks like so:
private void HtmlReadComplete(string data)
{
// do something with the data
}
I would like to be able to call GetHTMLDataAsync and then have it call HtmlReadComplete on the UI thread when the html has been read. I naively thought this could somehow be done with something that looks like
GetHTMLDataAsync(url).ContinueWith(HtmlReadComplete);
But, I can't get the syntax correct, nor am I even sure that's the appropriate way to handle it.
Thanks in advance!
public async void ProcessHTMLData(string url)
{
string HTMLData = await GetHTMLDataAsync(url);
HTMLReadComplete(HTMLData);
}
or even
public async void ProcessHTMLData(string url)
{
HTMLReadComplete(await GetHTMLDataAsync(url));
}
You're close, but ContinueWith() takes a delegate with Task as its parameter, so you can do:
GetHTMLDataAsync(url).ContinueWith(t => HtmlReadComplete(t.Result));
Normally, you should be careful with using Result together with async, because Result blocks if the Task hasn't finished yet. But in this case, you know for sure that the Task is complete, you Result won't block.

Passing an *Awaitable* Anonymous Function as a Parameter

Code first. This is what I'm trying to do. I'm close, but I think I just need to fix the way I've defined my parameter in the UpdateButton method.
private async void UpdateButton(Action<bool> post)
{
if (!await post())
ErrorBox.Text = "Error posting message.";
}
private void PostToTwitter()
{
UpdateButton(async () => await new TwitterAction().Post("Hello, world!"));
}
private void PostToFacebook()
{
UpdateButton(async () => await new FacebookAction().Post("Hello, world!"));
}
Unfortunately, the !await post() doesn't work because, "Type 'void' is not awaitable." So the question is, how do I define my parameter in this method to support an awaitable parameter?
Here's how the TwitterAction().Post() is defined...
public virtual async Task<bool> Post(string messageId){...}
private async void UpdateButton(Func<Task<bool>> post)
{
if (!await post())
ErrorBox.Text = "Error posting message.";
}
--EDIT--
UpdateButton(()=>Post("ss"));
private async void UpdateButton(Func<Task<bool>> post)
{
if (!await post())
this.Text = "Error posting message.";
}
public virtual async Task<bool> Post(string messageId)
{
return await Task.Factory.StartNew(() => true);
}
You need to pass this as a Task<bool>, not an Action<bool>.
This provides something that's "awaitable".
I believe this will work, given your current code:
private async Task UpdateButtonAsync(Task<bool> post)
{
if (!await post)
ErrorBox.Text = "Error posting message.";
}
// This will work if Post returns Task<bool> in the current API...
private void PostToTwitter()
{
UpdateButtonAsync(new TwitterAction().Post("Hello, world!"));
}
If you do not want to start the Task<bool> immediately, and need to keep it as passing a lambda, there is still no reason for the lambda to be async. In that case, you could use:
private async Task UpdateButtonAsync(Func<Task<bool>> post)
{
if (!await post())
ErrorBox.Text = "Error posting message.";
}
// This will work if Post returns Task<bool> in the current API...
private void PostToTwitter()
{
UpdateButtonAsync(() => new TwitterAction().Post("Hello, world!"));
}
This causes the lambda to return the Task<bool> (no async/await required, as Post already returns Task<bool>), and the update method to run the lambda.
Personally, I find the first option (above) simpler, and suspect it is more likely what you want. Given your API already returns Task<T>, you can just pass that around and await it directly.

How to work with HttpTaskAsyncHandler

public class FooHandler : HttpTaskAsyncHandler
{
public override async Task ProcessRequestAsync(HttpContext context)
{
return await new AdRequest().ProcessRequest();
// getting error here. "Return type of async type is void"
}
}
public class FooRequest
{
public async Task<String> ProcessRequest()
{
//return await "foo"; obviously nothing to wait here
}
}
I want to make a async handler and just want to return a string. How can i get this working? and is there a concise reference to work with Async methods and Tasks?
A few points:
You can await any Task, not just ones returned from async methods.
async methods wrap their returned value into a Task<TResult>; if there is no return value, they wrap the return itself into a Task.
There are several convenience methods available, e.g., Task.FromResult, if you don't need the overhead of an async method.
Only make a method async if you have to use await in it. If you don't need to make the method async, don't.
You may find my async/await intro helpful.
public class FooHandler : HttpTaskAsyncHandler
{
public override Task ProcessRequestAsync(HttpContext context)
{
return new AdRequest().ProcessRequest();
}
}
public class AdRequest
{
public Task<String> ProcessRequest()
{
return Task.FromResult("foo");
}
}
You shouldn't "return" the Task, the compiler will do it implicitly as it is an async function:
public override async Task ProcessRequestAsync(HttpContext context)
{
await new AdRequest().ProcessRequest();
}
public async Task<String> ProcessRequest()
{
return "foo";
}
This is another way, closer to what you were trying to do: (without async/await)
public override Task ProcessRequestAsync(HttpContext context)
{
return new AdRequest().ProcessRequest();
}
public Task<String> ProcessRequest()
{
return Task.Return("foo");
}
A general reference to async is here
Essentially adding the async modifier to a method, makes it return a Task implicitly. If you return an int, it will turn it into a Task<int>. await does the opposite, turning a Task<int> into an int.
This is a truly asynchronous method:
public Task<string> ProcessRequest()
{
var textFile = File.OpenText("file.txt");
var readTask = textFile.ReadToEndAsync();
readTask.ContinueWith(previousTask => textFile.Dispose());
return readTask;
}
If you run this method with a large file or a file on a slow drive the execution will return to caller long before file reading ends. In Stephen Cleary's example the caller will get back control only when the result ("foo") is finished calculating.
Dispose must be in ContinueWith because the method execution will return to caller before file reading is complete so file can't be closed in ProcessRequest method.
One can of course start their own task.
public Task<string> ProcessRequest(CancellationToken cancellationToken)
{
var readTask = Task.Run(() =>
{
using (var textFile = File.OpenText("file.txt"))
{
var text = textFile.ReadToEnd();
cancellationToken.ThrowIfCancellationRequested();
var processedText = text.Replace("foo", "bar");
return processedText;
}
});
return readTask;
}
It is a good practice to have a CancellationToken and periodically check if cancellation was requested to allow long running operarions to be cancelled.
Edit 1
As #Stephen Cleary highlighted the first sample and this result in approximately or maybe exactly the same CIL:
public async Task<string> ProcessRequest()
{
using (var textFile = File.OpenText("file.txt"))
{
var s = await textFile.ReadToEndAsync();
return s;
}
}
Basically the compiler will transform the code following await textFile.ReadToEndAsync() into ContinueWith.
Each syntax has its benefits, my preference is that 1-2 lines (i.e. dispose and log) go into ContinueWith, more complex continuation uses await.

Categories