How do I convert this chain of synchronous method calls into async (using the async/await operators)? Given that only the last call, DoRequest(), is the one that takes time to execute, is that the only method that needs to become async? Or do all the callers in the chain, RequestSomething() and Process(), need to be made async as well?
[HttpGet]
void Process()
{
var url = "http://someapi.com";
var myObject= RequestSomething(url);
//do something with the myObject.
}
MyObject RequestSomething(string url)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
var response = DoRequest(request);
return JsonConvert.DeserializeObject<MyObject>(response);
}
//method that takes time to return.
HttpResponseMessage DoRequest(HttpRequestMessage request)
{
var client = new HttpClient();
return client.SendAsync(request).Result;
}
To do async correctly it is "infectious", if you do it in one spot you need to do it all the way up the call chain to get any of the real benefits out of it. So whatever is calling Process() will need to handle the task returned from Process by either awaiting it or passing it up the chain like DoRequest does.
async Task Process()
{
var url = "http://someapi.com";
var myObject= await RequestSomething(url);
//do something with the myObject.
}
async Task<MyObject> RequestSomething(string url)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
var response = await DoRequest(request).ConfigureAwait(false);
return JsonConvert.DeserializeObject<MyObject>(response);
}
//method that takes time to return.
Task<HttpResponseMessage> DoRequest(HttpRequestMessage request)
{
var client = new HttpClient();
return client.SendAsync(request);
}
Because you do not do any extra work after performing the request you don't need async/await in your DoRequest function, but the other ones will need the async/await keywords. The .ConfigureAwait(false) makes it so that function does not have to run the rest of its code on the UI thread, this can give you a small performance boost. I did not know if the code that is in //do something with the myObject. required you being on the UI thread or not, so I did not put it on that await, but if you don't need to be on the UI thread you could add it there too.
You should make DoRequest...
Public async Task<HttpResponseMessage> DoRequest(...
Then return await client.SendAsync
Similarly, DoSomething should be async Task<...>
Your calling method, Process can be async void if you want to call it as fire and forget, otherwise an async Task.
Try:
var task = Task.Run(() => RequestSomething());
var task2 = Task.Run(() => RequestSomethingElse());
await Task.WhenAll(task, task2);
You can specify more than two if you like, or only one.
Related
In my C# app, I've been able to start concurrent tasks that make http calls and then wait for all of them to complete.
var responseTasks = new List<Task<HttpResponseMessage>>();
// PostChanges makes an async http call and returns the task
responseTasks.Add(myServiceClient.PostChanges(json));
responseTasks.Add(myServiceClient.PostChanges(json));
responseTasks.Add(myServiceClient.PostChanges(json));
Task.WaitAll(responseTasks.ToArray());
But what if each of the calls to PostChanges relies on a call that gets data from an http request? What is the simplest way to create a method that encapsulates the two calls and returns a started task? The method would have to make both requests something like this:
public Task<HttpResponseMessage> GetDataAndPostChanges(MyInput input)
{
var json = myServiceClient.GetData(input); // this call must complete first
var response = myServiceClient.PostChanges(json);
return response; // how to actually return a task immediately that does both calls?
}
I would then want to make concurrent calls to that method and wait for all of them to complete.
var responseTasks = new List<Task<HttpResponseMessage>>();
// PostChanges makes an async http call and returns the task
responseTasks.Add(myServiceClient.GetDataAndPostChanges(input1));
responseTasks.Add(myServiceClient.GetDataAndPostChanges(input2));
responseTasks.Add(myServiceClient.GetDataAndPostChanges(input3));
Task.WaitAll(responseTasks.ToArray());
Make the function async and await the necessary calls
public async Task<HttpResponseMessage> GetDataAndPostChanges(MyInput input) {
var json = await myServiceClient.GetData(input); // this call must complete first
var response = await myServiceClient.PostChanges(json);
return response;
}
The assumption above is that GetData is an asynchronous function.
I have a Web API service that is used to retrieve and update a specific set of data (MyDataSet objects), and I am running into some confusion in using async/await when performing the following events:
Update MyDataSet with new values
Get MyDataSet (but only after the new values have been updated)
In my client, I have something similar to the following:
Harmony.cs
private async Task<string> GetDataSet(string request)
{
using(var httpClient = new HttpClient())
{
httpClient.baseAddress = theBaseAddress;
HttpResponseMessage response = await httpClient.GetAsync(request);
response.EnsureSuccessStatusCode();
return response.Content.ReadAsStringAsync().Result;
}
}
private async Task PostDataSet<T>(string request, T data)
{
using (var httpClient = new HttpClient())
{
client.BaseAddress = new Uri(theBaseAddress);
HttpResponseMessage response = await client.PostAsJsonAsync<T>(request, data);
response.EnsureSuccessStatusCode();
}
}
internal MyDataSet GetMyDataSetById(int id)
{
string request = String.Format("api/MyDataSet/GetById/{0}", id);
return JsonConvert.DeserializeObject<MyDataSet>(GetDataSet(request).Result);
}
internal void UpdateDataSet(MyDataSet data)
{
PostDataSet("api/MyDataSet/Update", data);
}
HarmonyScheduler.cs
internal void ScheduleDataSet()
{
MyDataSet data = ...
harmony.UpdateDataSet(data);
MyDataSet data2 = harmony.GetMyDataSetById(data.Id);
}
There is a compiler warning in Harmony.cs UpdateDataSet because the call is not awaited and execution will continue before the call is completed. This is affecting the program execution, because data2 is being retrieved before the update is taking place.
If I were to make UpdateDataSet async and add an await to it, then it just moves things up the stack a level, and now HarmonyScheduler gets the warning about not being awaited.
How do I wait for the update to be complete before retrieving data2, so that I will have the updated values in the data2 object?
How do I wait for the update to be complete before retrieving data2,
so that I will have the updated values in the data2 object?
The thing I see many people don't comprehend is the fact that using the TAP with async-await will infect your code like a plague.
What do I mean by that?
Using the TAP will cause async to bubble up all the way to the top of your call stack, that is why they say async method go "all the way". That is the recommendation for using the pattern. Usually, that means that if you want to introduce an asynchronous API, you'll have to provide it along side a separate synchronous API. Most people try to mix and match between the two, but that causes a whole lot of trouble (and many SO questions).
In order to make things work properly, you'll have to turn UpdateDataSet and GetMyDataSetById to be async as well. The outcome should look like this:
private readonly HttpClient httpClient = new HttpClient();
private async Task<string> GetDataSetAsync(string request)
{
httpClient.BaseAddress = theBaseAddress;
HttpResponseMessage response = await httpClient.GetAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
private async Task PostDataSetAsync<T>(string request, T data)
{
client.BaseAddress = new Uri(theBaseAddress);
HttpResponseMessage response = await client.PostAsJsonAsync<T>(request, data);
response.EnsureSuccessStatusCode();
}
internal async Task<MyDataSet> GetMyDataSetByIdAsync(int id)
{
string request = String.Format("api/MyDataSet/GetById/{0}", id);
return JsonConvert.DeserializeObject<MyDataSet>(await GetDataSetAsync(request));
}
internal Task UpdateDataSetAsync(MyDataSet data)
{
return PostDataSetAsync("api/MyDataSet/Update", data);
}
Note - HttpClient is meant to be reused instead of a disposable, single call object. I would encapsulate as a class level field and reuse it.
If you want to expose a synchronous API, do so using a HTTP library that exposes a synchronous API, such as WebClient.
Always wait on the tasks right before you need its results.
Its always better to wait on a task if it contains an await in its body.
Warning : Do not use .Wait() or .Result
You would understand the concept better if you go through the control flow as explained Control Flow in Async Programs.
So in your case I would make all the functions accessing the async methods GetDataSet(string request) and PostDataSet<T>(string request, T data) with return type Task as awaitable.
Since you dont seem to expect any result back from the PostDataSet function you could just wait for it to complete.
PostDataSet("api/MyDataSet/Update", data).Wait();
I know it has been asked a lot, but my problem is, that my method won't wait for the request to be completet, even though i have implemented a TaskCompletionSource, which should have done the job, but it doesn't.
public DecksViewModel(bool local)
{
DList = new List<Deck>();
if (local)
InitializeLocalDeckList();
else
{
Dereffering();
}
}
public async void Dereffering()
{
var e = await InitilaizeWebDeckList();
List<DeckIn> decksIn = JsonConvert.DeserializeObject<List<DeckIn>>(e);
foreach (DeckIn d in decksIn)
{
Deck dadd = new Deck();
dadd.CardCount = 0;
dadd.Name = d.name;
dadd.PicturePath = d.image;
dadd.InstallDirectory = false;
DList.Add(dadd);
}
DataSource = AlphaKeyGroup<Deck>.CreateGroups(DList, System.Threading.Thread.CurrentThread.CurrentUICulture, (Deck s) => { return s.Name; }, true);
}
public Task<String> InitilaizeWebDeckList()
{
var tcs = new TaskCompletionSource<string>();
var client = new RestClient("blabla.com");
var request = new RestRequest("");
request.AddHeader("Authorization", "Basic blabla");
client.ExecuteAsync(request, response =>
{
test = response.Content;
tcs.SetResult(response.Content);
});
return tcs.Task;
}
So when I call the DecksViewModel constructor, I asyncally try to request the data from a webserver and fill the model.
The point is, that the corresponding view "doesn't wait" for the request to fill the model, so it's displayed empty.
I use the
List<AlphaKeyGroup<Deck>> DataSource
to fill a LongListSelector via DataBinding. But DataSource isn't yet set, when it is binded.
I hope you can help
You're calling an async method without awaiting it inside the constructor. That's why "it doesn't wait" (because it has nothing to wait on).
It's usually a bad idea to call an async method inside the constructor for that reason combined with the fact that constructors can't be async.
You should redesign your solution accordingly. An option is to have an async static method that creates an instance and awaits the procedure:
public static async Task CreateInstance(bool local)
{
var model = new DecksViewModel();
if (local)
{
await InitializeLocalDeckList();
}
else
{
await Dereffering();
}
}
That would allow you to not use async void which should only be used in UI even handlers.
You can read more about other options in Stephen Cleary's blog
You are using async void, which means nobody's gonna wait for that. It's just fire and forget.
I see some misunderstanding in the async keyword here:
Your code will only wait for the result of an async method, if you use await. Otherwise that call will just start the async method, but you don't know when it is actually gonna run.
You cannot use await in constructors though.
I'm using .NET 4.0, so I can't use the async/await keywords.
After I laboriously set up tasks and continuations instead of just calling .Result, all I got for my efforts was a mess and it runs 46% slower on a workload of a few dozen HTTP GETs. (I get similar perf degradation if I call the workload in serial or in a Parallel loop)
What must I do to see any performance benefit?
//Slower code
public UserProfileViewModel GetAsync(Guid id)
{
UserProfileViewModel obj = null;//Closure
Task result = client.GetAsync(id.ToString()).ContinueWith(responseMessage =>
{
Task<string> stringTask = responseMessage.Result
.Content.ReadAsStringAsync();
Task continuation = stringTask.ContinueWith(responseBody =>
{
obj = JsonConvert
.DeserializeObject<UserProfileViewModel>(responseBody.Result);
});
//This is a child task, must wait before returning to parent.
continuation.Wait();
});
result.Wait();
return obj;
}
//Faster code
public UserProfileViewModel GetSynchr(Guid id)
{
//Asych? What's is that?
HttpResponseMessage response = client.GetAsync(id.ToString()).Result;
string responseBody = response.Content.ReadAsStringAsync().Result;
return JsonConvert.DeserializeObject<UserProfileViewModel>(responseBody);
}
You are using "async" methods but doing everything synchronously. That certainly won't be any better than doing everything synchronously with the synchronous methods.
Take a look at this:
public Task<UserProfileViewModel> GetAsync(Guid id)
{
var uri = id.ToString();
return client.GetAsync(uri).ContinueWith(responseTask =>
{
var response = responseTask.Result;
return response.Content.ReadAsStringAsync().ContinueWith(jsonTask =>
{
var json = jsonTask.Result;
return JsonConvert.DeserializeObject<UserProfileViewModel>(json);
});
}).Unwrap();
}
Notice how the method returns a Task and the continuations are returned from the method. This allows your method to return almost instantly, giving the caller a handle to the running work and whatever continuations need to happen. The returned task will only be complete once everything is done, and it's result will be your UserProfileViewModel.
The Unwrap method takes a Task<Task<UserProfileViewModel>> and turns it into a Task<UserProfileViewModel>.
I have a bunch of slow functions that are essentially this:
private async Task<List<string>> DownloadSomething()
{
var request = System.Net.WebRequest.Create("https://valid.url");
...
using (var ss = await request.GetRequestStreamAsync())
{
await ss.WriteAsync(...);
}
using (var rr = await request.GetResponseAsync())
using (var ss = rr.GetResponseStream())
{
//read stream and return data
}
}
This works nicely and asynchronously except for the call to WebRequest.Create - this single line freezes the UI thread for several seconds which sort of ruins the purpose of async/await.
I already have this code written using BackgroundWorkers, which works perfectly and never freezes the UI.
Still, what is the correct, idiomatic way to create a web request with respect to async/await? Or maybe there is another class that should be used?
I've seen this nice answer about asyncifying a WebRequest, but even there the object itself is created synchronously.
Interestingly, I'm not seeing a blocking delay with WebRequest.Create or HttpClient.PostAsync. It might be something to do with DNS resolution or proxy configuration, although I'd expect these operations to be implemented internally as asynchronous, too.
Anyway, as a workaround you can start the request on a pool thread, although this is not something I'd normally do:
private async Task<List<string>> DownloadSomething()
{
var request = await Task.Run(() => {
// WebRequest.Create freezes??
return System.Net.WebRequest.Create("https://valid.url");
});
// ...
using (var ss = await request.GetRequestStreamAsync())
{
await ss.WriteAsync(...);
}
using (var rr = await request.GetResponseAsync())
using (var ss = rr.GetResponseStream())
{
//read stream and return data
}
}
That would keep the UI responsive, but it might be difficult to cancel it if user wants to stop the operation. That's because you need to already have a WebRequest instance to be able to call Abort on it.
Using HttpClient, cancellation would be possible, something like this:
private async Task<List<string>> DownloadSomething(CancellationToken token)
{
var httpClient = new HttpClient();
var response = await Task.Run(async () => {
return await httpClient.PostAsync("https://valid.url", token);
}, token);
// ...
}
With HttpClient, you can also register a httpClient.CancelPendingRequests() callback on the cancellation token, like this.
[UPDATE] Based on the comments: in your original case (before introducing Task.Run) you probably did not need the IProgress<I> pattern. As long as DownloadSomething() was called on the UI thread, every execution step after each await inside DownloadSomething would be resumed on the same UI thread, so you could just update the UI directly in between awaits.
Now, to run the whole DownloadSomething() via Task.Run on a pool thread, you would have to pass an instance of IProgress<I> into it, e.g.:
private async Task<List<string>> DownloadSomething(
string url,
IProgress<int> progress,
CancellationToken token)
{
var request = System.Net.WebRequest.Create(url);
// ...
using (var ss = await request.GetRequestStreamAsync())
{
await ss.WriteAsync(...);
}
using (var rr = await request.GetResponseAsync())
using (var ss = rr.GetResponseStream())
{
// read stream and return data
progress.Report(...); // report progress
}
}
// ...
// Calling DownloadSomething from the UI thread via Task.Run:
var progressIndicator = new Progress<int>(ReportProgress);
var cts = new CancellationTokenSource(30000); // cancel in 30s (optional)
var url = "https://valid.url";
var result = await Task.Run(() =>
DownloadSomething(url, progressIndicator, cts.Token), cts.Token);
// the "result" type is deduced to "List<string>" by the compiler
Note, because DownloadSomething is an async method itself, it is now run as a nested task, which Task.Run transparently unwraps for you. More on this: Task.Run vs Task.Factory.StartNew.
Also check out: Enabling Progress and Cancellation in Async APIs.
I think you need to use HttpClient.GetAsync() which returns a task from an HTTP request.
http://msdn.microsoft.com/en-us/library/hh158912(v=vs.110).aspx
It may depend a bit on what you want to return, but the HttpClient has a whole bunch of async methods for requests.