I have this method that downloads data from the internet:
public ObservableCollection<Magnetka> ParseJSON() {
ObservableCollection<Models.Magnetka> Markers = new ObservableCollection<Models.Magnetka>();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("(URL)");
request.Headers.Add("Authentication-Token", "(KEY)");
request.Method = "GET";
request.ContentType = "application/json";
CookieContainer cookieContainer = new CookieContainer();
request.CookieContainer = cookieContainer;
var response = request.GetResponse();
var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
......... JSON parsing)
return Markers;
Now I found out this isn't too fast and it freezes the app for two seconds or so, so I need to make it async. However, I call the method like this in another class:
GetMarkers getMarkers = new GetMarkers();
Markers = getMarkers.ParseJSON();
How can I make my method async? I don't understand the concept well and if I make ParseJSON() async Task, I don't know where to put the "await" keyword.
Can anyone help me?
first, mark the method async and use the async version of GetResponse
public async Task<ObservableCollection<Magnetka>> ParseJSON()
{
...
var response = await request.GetResponseAsync();
...
return Markers;
}
then when you call it use the await keyword
Markers = await getMarkers.ParseJSON();
You would change your method to something like this instead:
public async Task<ObservableCollection<Magnetka>> ParseJSON()
{
var markers = new ObservableCollection<Magnetka>();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("(URL)");
request.Headers.Add("Authentication-Token", "(KEY)");
request.Method = "GET";
request.ContentType = "application/json";
CookieContainer cookieContainer = new CookieContainer();
request.CookieContainer = cookieContainer;
using var response = await request.GetResponseAsync().ConfigureAwait(false);
using var responseStream = response.GetResponseStream();
using var streamReader = new StreamReader(responseStream);
var json = await streamReader.ReadToEndAsync().ConfigureAwait(false);
// json parsing
return markers;
}
Firstly, you would mark your method async. This signals the compiler to set up a state machine for this method for continuation. Nothing async happening yet though.
Another thing you need to do is to wrap your return value in a Task, which is similar to a promise in some other languages. Tasks are await-able, which is where the asynchronous stuff is happening.
Switching from using the synchronous methods in WebRequest to use the Async variants and awaiting their results makes your method async.
So from:
request.GetResponse()
To:
await request.GetResponseAsync()
Which returns a awaitable Task<WebResponse>.
The same goes for the stream reader code, you can use its async API too.
A tricky thing by using asynchronous code is that, it kind of forces you to do this all the way up. However, there are exceptions for lifecycle methods and events, where you don't necessarily have to change the signature to return a Task.
Another thing you might notice here. I've added ConfigureAwait(false) at the end of the async method calls. I've done this because out of the box, after returning from an awaited method, the code will try to return to the originating context. This can lead to a couple of issues such as dead-locks if the originating context is the UI Thread. Or, it can lead to bad performance trying to switch between context a lot. Adding ConfigureAwait(false) will just return on the same context the await was run on, which is fine as long as we don't need or expect to return on that context.
As for where you call ParseJson() is probably equally important. Consider deferring it to some lifecycle method or event where you are not doing much else.
This could be OnResume() on Android or ViewWillAppear on iOS. If you are using MVVM, use a Command to encapsulate it.
Related
When i'm sending a http request with my app, the UI of my app freezes while i'm waiting for the http response.
Why is this happening?
async void HttpAction()
{
var request = HttpWebRequest.Create(url);
request.Method = "GET";
request.Timeout = 1500;
await Task.Delay(500);
using (HttpWebResponse reponse = request.GetResponse() as HttpWebResponse)
{
if (reponse.StatusCode == HttpStatusCode.OK)
{
//UI action
reponse.Close();
}
}
Thanks a lot!
You need to await the HttpAction method at the calling point. Go through this Async/Await article first for better understanding of Asynchronous programming.
There should not be any UI action inside Asynchronous Task rather return the result to the calling point and then perform the necessary actions.
async Task<bool> GetHttpAsyncStatus()
{
var request = HttpWebRequest.Create(url);
request.Method = "GET";
request.Timeout = 1500;
using (HttpWebResponse reponse = await request.GetResponseAsync() as HttpWebResponse)
{
if (reponse.StatusCode == HttpStatusCode.OK)
{
// no UI Action, just return the result
reponse.Close();
return true;
}
return false;
}
}
This must be invoked as
bool requestStatus = await GetHttpAsyncStatus();
You need to use AsyncTask and run your http calls in the doInBackground method.
And inside the onPostExecute method, you update the ui. AsyncTask is not terribly involved and will free up the ui thread. I don't have much experience with async but AsyncTask and HttpurlConnection has never failed me on the more tricky stuff.
I'm trying to use the Azure media services with REST api, in a xamarin shared project on visual studio 2013. This is the code i use to get the access token.
public HttpFactory()
{
string token = GetToken(serviceURI).Result;
//some more methods also async tasks
}
private async Task<string> GetToken(Uri serviceUri)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(serviceURI);
request.Accept = "application/json";
request.Method = "GET";
request.Headers["Host"] = "media.windows.net";
request.Headers["x-ms-version"] = "2.9";
request.Headers["Authorization"] = "Bearer " + token;
var response = (HttpWebResponse) await request.GetResponseAsync();
if (response.StatusCode == HttpStatusCode.MovedPermanently)
{
serviceURI = new Uri(response.Headers["Location"]);
HttpWebRequest req = ( HttpWebRequest)WebRequest.Create(serviceURI);
var res = (HttpWebResponse)await req.GetResponseAsync();
using (Stream responseStream = res.GetResponseStream())
{
using (StreamReader reader = new StreamReader(responseStream))
{
string str = reader.ReadToEnd();
// var test = JsonConvert.DeserializeObject(str);
JToken jtoken = JsonConvert.DeserializeObject<JToken>(str);
return jtoken["access_token"].Value<string>();
}
}
}
return "";
}
But when the compiler reaches -
var response = (HttpWebResponse) await request.GetResponseAsync();
it skips the rest of the code, and i never get the response. I know the code is working - because it works just fine without the task, in a async void method.
Anyone knows how to fix this, or am i doing something wrong? I also tried this in vs2015 but its the same.
You have a deadlock over the UI thread.
You're blocking the thread with Task.Result when it is needed to complete the async method which will complete the task that it's waiting on.
That's why you shouldn't block synchronously on asynchronous code. You should await the task returned from GetToken instead:
string token = await GetToken(serviceURI);
If you can't use async in that method, either move that logic to a different method (e.g. OnLoad event handler).
Another solution would be to use ConfigureAwait on the GetResponseAsync task and so the rest of the method wouldn't run on the UI thread, avoiding the deadlock:
var response = (HttpWebResponse) await request.GetResponseAsync().ConfigureAwait(false);
I have a Web API application which exposes certain async methods, such as:
public async Task<HttpResponseMessage> Put(string id){
var data = await Request.Content.ReadAsStringAsync();
//do something to put data in db
return Request.CreateResponse(HttpStatusCode.Ok);
}
I also have a class library that consumes this API with System.Net.WebRequest like this:
var request = (HttpWebRequest)WebRequest.Create(url);
var response = await request.GetResponseAsync();
Does the response need to be retrievend async? If so, why? Or can I also use request.GetResponse();.
I used the GetResponse before which would sometimes throw a 500 error ( An asynchronous module or handler completed while an asynchronous operation was still pending). Once I changed it to GetResponseAsync(), I stopped receiving this error.
EDIT: Code that sometimes throws a 500 error
I had the web api method stripped down to (just to check whether the 500 error was business logic or something else). Note: this is after changing the consumer to async (first function is the consumer, then the api method):
public HttpWebResponse(GetApiResponseForPut(string url, string putData, NameValueCollection headers)
{
var request = (HttpWebRequest)WebRequest.Create(url);
request.CookieContainer = _cookieContainer;
request.Method = "PUT";
if (headers != null)
{
request.Headers.Add(headers);
}
var encoding = new ASCIIEncoding();
var byte1 = new byte[0];
if (putData != null)
{
byte1 = encoding.GetBytes(putData);
}
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byte1.Length;
Stream requestStream = request.GetRequestStream();
requestStream.Write(byte1, 0, byte1.Length);
requestStream.Close();
var response = await request.GetResponseAsync();
return (HttpWebResponse)response;
}
public async Task<HttpResponseMessage> Put(string id)
{
HttpResponseMessage response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
Does the response need to be retrievend async? If so, why?
No, it doesn't. You need to separate how the server-side operates and how the client queries your endpoint. When you expose a method which is async Task, you state that in your server-side calls, you're making async calls. This is transparent to the caller, all he gets is an API endpoint, he doesn't have any knowledge of your internal implementation.
Remember, even if you use async on the server-side, the request will only return to the caller once complete.
We have existing iOS application developed using Xamarin.Forms. Now we want to extend to both Android and Windows Phone. In the existing application, all the web service calls are made synchronously. Windows phone supports only asynchronous calling, so we thought of wrapping the asynchronous calls to synchronous.
We are using HttpClient.PostAsync method to access the service. Once the execution hits PostAsync method, the phone hangs. The code to call the service is as follows:
private static async void CallService(System.Uri uri)
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Host = uri.Host;
client.Timeout = System.TimeSpan.FromSeconds(30);
HttpContent content = new StringContent("", Encoding.UTF8, "application/xml");
var configuredClient = client.PostAsync(uri, content).ConfigureAwait(false);
var resp = configuredClient.GetAwaiter().GetResult();
resp.EnsureSuccessStatusCode();
responseString = resp.StatusCode.ToString();
resp.Dispose();
client.CancelPendingRequests();
client.Dispose();
}
}
I know this is because of blocking the UI thread, so only I implemented ConfigureAwait(false) but that didn't work at all. I tried with System.Net.WebClient also but the same result.
Now, how I will make this asynchronous call to process synchronously in Windows Phone 8?
First of all avoid using async void methods,because you can't easily wait for its completion. Return Task instead, being inside async method you don't need to do something special to return a Task. Compiler does all the work for you.
You need to await the call to HttpClient.PostAsync, that should be enough to keep the UI responsive.
private static async Task CallService(System.Uri uri)
{
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Host = uri.Host;
client.Timeout = System.TimeSpan.FromSeconds(30);
HttpContent content = new StringContent("", Encoding.UTF8, "application/xml");
var resp = await client.PostAsync(uri, content);// Await it
resp.EnsureSuccessStatusCode();
responseString = resp.StatusCode.ToString();
resp.Dispose();
client.CancelPendingRequests();
}
}
Note: I've removed the ConfigureAwait(false) as that's not required. If you really need it, you may add it back.
I am new to the async world of C# and admittedly don't have a lot of knowledge on the subject. I just had to implement it in one of our services and I am a little confused on how it works. I want to POST to an API as fast as absolutely possible. I'm less interested in how long it takes to get the response and do stuff with it. Here's an example of what I'm doing.
foreach (var item in list)
{
callPostFunction(item.data);
log("Posted");
}
public async void callPostFunction(PostData data)
{
var apiResult = await postToAPI(data);
updateDB(apiResult);
}
public static async Task<string> postToAPI(PostData dataToSend)
{
var url = "Example.com";
HttpWebRequest httpWReq = (HttpWebRequest)WebRequest.Create(url);
ASCIIEncoding encoding = new ASCIIEncoding();
string postData = dataToSend;
byte[] dataBytes = encoding.GetBytes(postData);
httpWReq.Method = "POST";
httpWReq.ContentType = "application/x-www-form-urlencoded";
httpWReq.ContentLength = dataBytes.Length;
httpWReq.Accept = "application/json, application/xml, text/json, text/x-json, text/javascript, text/xml";
using (var stream = await httpWReq.GetRequestStreamAsync())
{
await stream.WriteAsync(dataBytes, 0, dataBytes.Length);
}
HttpWebResponse response = (HttpWebResponse)httpWReq.GetResponse();
return new StreamReader(response.GetResponseStream()).ReadToEnd();
}
What happens is if I put 1000 items in the list, "Posted" is logged 1000 times immediately. Great, the process is done and can continue doing other things. The problem is the somewhere in the background the callPostFunction is calling postToAPI and posting to the API, it works, but it takes a long time. It takes about as long (although not as long) as it did before implementing async. I feel like I'm missing something.
I have to hit the API as fast. Ideally I'd like to hit it as often as the callPostFunction is getting called, nearly immediately. How might I go about doing that?
Set ServicePointManager.DefaultConnectionLimit to int.MaxValue.
Also, as #Servy pointed out, avoid async void.
You're not missing something. It takes time to post a bunch of data. Network connections are slow. They have limited bandwidth. Doing this work asynchronously is no magic bullet for speed. It's purpose is not to optimize speed either.