UI blocked by Task - c#

I have the following code that gets data from a site
private void BeginCreationButton_Click(object sender, RoutedEventArgs e)
{
//Log("INFO", "Beggining ad creation");
GeneralProgressBar.Visibility = Visibility.Visible;
foreach(var ad in ads)
{
string adUh = string.Empty;
string errorMsg = string.Empty;
bool error = false;
//Task<string> uhFetch = Task<string>.Factory.StartNew(() => GetUhForAdvert());
Task task = Task.Factory.StartNew(() =>
{
HttpWebRequest newPromoRequest = WebRequest.Create("https://www.randomsite.com") as HttpWebRequest;
newPromoRequest.CookieContainer = Cookies;
newPromoRequest.Method = "GET";
newPromoRequest.Accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8";
string uh = string.Empty;
HttpWebResponse response = (HttpWebResponse)newPromoRequest.GetResponse();
if (response.StatusCode == HttpStatusCode.OK)
{
using (Stream s = response.GetResponseStream())
{
using (StreamReader sr = new StreamReader(s, Encoding.GetEncoding(response.CharacterSet)))
{
HtmlDocument doc = new HtmlDocument();
doc.Load(sr);
adUh = doc.DocumentNode.SelectNodes("//form")[0].SelectNodes("//input")[0].Attributes[2].Value;
}
}
}
});
try
{
task.Wait();
}
catch (AggregateException ae)
{
ae.Handle((x) =>
{
errorMsg = x.Message + " | " + x.StackTrace;
error = true;
return error;
});
}
if (error)
{
Log("ERROR", errorMsg);
}
Log("INFO", adUh);
}
}
However, while the task is executing, the UI is getting blocked but I'm not sure why it's happening. Not sure if it's the stream reading or HTML processing part as I'm using the Request code on other parts of my project without those two componentss and it works like a charm.

Don't block your UI thread using task.Wait();. Make your button click handler async like this:
private async void BeginCreationButton_Click(object sender, RoutedEventArgs e)
{
// code here...
}
...and where you call:
task.Wait();
...call it like this:
await task;
Also, you shouldn't need to use Task.Factory.StartNew, you could simplify by just using (HttpWebResponse)await newPromoRequest.GetResponseAsync(); and wrapping all the code of your task inside your try catch block.
EDIT: You can read Stephen Cleary's blog for a detailed explanation about this subject.

Related

async task cancellation c# xamarin

I have a functionality of search users. I have provided a textview and on that textview changed method I'm firing a method to get data from web server. But I'm facing problem when user types letter, because all the api hits done in async task. Service should be hit after 100 milli-sec of wait, means if user types a letter "a" then doesn't type for 100 milli-sec then We have to hit the service. But if user types "a" then "b" then "c", so one service should be hit for "abc", not for all.
I followed the official link, but it doesn't help me
https://msdn.microsoft.com/en-us/library/jj155759.aspx
So basically here is my code
textview.TextChange+= (sender,e) =>{
CancellationTokenSource cts = new CancellationTokenSource();
await Task.Delay(500);
// here some where I have to pass cancel token
var lst = await APIClient.Instance.GetUserSearch("/user/get?searchTerm=" + newText, "application/json",cts);
if (lst != null && lst.Count > 0){
lstSearch.AddRange(lst);
}
}
Here is my method to GetUser
public async Task<JResponse> GetUserSearch<JResponse>(string uri, string contentType,CancellationToken cts)
{
try
{
Console.Error.WriteLine("{0}", RestServiceBaseAddress + uri);
string url = string.Format("{0}{1}", RestServiceBaseAddress, uri);
var request = (HttpWebRequest)WebRequest.Create(url);
request.ContentType = contentType;
if (Utility.CurrentUser != null && !string.IsNullOrWhiteSpace(Utility.CurrentUser.AuthToken))
{
request.Headers.Add("api_key", Utility.CurrentUser.AuthToken);
}
request.Method = "POST";
var payload = body.ToString();
request.ContentLength = payload.Length;
byte[] byteArray = Encoding.UTF8.GetBytes(body.ToString());
request.ContentLength = byteArray.Length;
using (var stream = await request.GetRequestStreamAsync())
{
stream.Write(byteArray, 0, byteArray.Length);
stream.Close();
}
using (var webResponse = await request.GetResponseAsync())
{
var response = (HttpWebResponse)webResponse;
using (var reader1 = new StreamReader(response.GetResponseStream()))
{
Console.WriteLine("Finished : {0}", uri);
var responseStr = reader1.ReadToEnd();
var responseObj = JsonConvert.DeserializeObject<JResponse>(
responseStr,
new JsonSerializerSettings()
{
MissingMemberHandling = MissingMemberHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore
});
return responseObj;
}
}
}
catch (System.Exception ex)
{
Utility.ExceptionHandler("APIClient", "ProcessRequestAsync", ex);
}
return default(JResponse);
}
In your example, you are creating a CancellationTokenSource - you need to hold a reference to it, so that the next time the handler is invoked, the previous search can be cancelled. Here is an example console app that you should be able to run, but the important piece is in the handler.
private CancellationTokenSource _cts;
private async void TextChangedHandler(string text) // async void only for event handlers
{
try
{
_cts?.Cancel(); // cancel previous search
}
catch (ObjectDisposedException) // in case previous search completed
{
}
using (_cts = new CancellationTokenSource())
{
try
{
await Task.Delay(TimeSpan.FromSeconds(1), _cts.Token); // buffer
var users = await _userService.SearchUsersAsync(text, _cts.Token);
Console.WriteLine($"Got users with IDs: {string.Join(", ", users)}");
}
catch (TaskCanceledException) // if the operation is cancelled, do nothing
{
}
}
}
Be sure to pass the CancellationToken into all of the async methods, including those that perform the web request, this way you signal the cancellation right down to the lowest level.
Try to use timer. First time then you change text - you create it. Then you change text after that - you restart timer. If you don't change text for 700 milliseconds - timer will fire PerformeSearch method. Use Timeout.Infinite for timer period parameter to prevent it from restarting.
textview.TextChange += (sender,e) =>
{
if (_fieldChangeTimer == null)
_fieldChangeTimer = new Timer(delegate
{
PerformeSearch();
}, null, 700, Timeout.Infinite);
else
{
_fieldChangeTimer.Change(700, Timeout.Infinite);
}
};
Instantiate the CancellationTokenSource.
cts = new CancellationTokenSource(); Example method
private void cancelButton_Click(object sender, RoutedEventArgs e)
{
if (cts != null)
{
cts.Cancel();
}
}

Call UI thread in HttpWebRequest.BeginGetResponse

Good night, I'm trying to call the UI thread, to update a proprety actualy, inside HttpWebRequest.BeginGetResponse method by using CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync method. But it does not work, I do not know how it works actually. There is my code:
private void DebutReponse(IAsyncResult resultatAsynchrone)
{
HttpWebRequest requete = (HttpWebRequest)resultatAsynchrone.AsyncState;
Stream postStream = requete.EndGetRequestStream(resultatAsynchrone);
string donneesAEnvoyer = "username=" + Name + "&password=" + Password + "&email=" + Email;
string lol = donneesAEnvoyer;
byte[] tableau = Encoding.UTF8.GetBytes(donneesAEnvoyer);
postStream.Write(tableau, 0, donneesAEnvoyer.Length);
postStream.Close();
requete.BeginGetResponse(FinReponse, requete);
}
private void FinReponse(IAsyncResult resultatAsynchrone)
{
HttpWebResponse aResp = null;
try
{
HttpWebRequest requete = (HttpWebRequest)resultatAsynchrone.AsyncState;
WebResponse webResponse = requete.EndGetResponse(resultatAsynchrone);
Stream stream = webResponse.GetResponseStream();
StreamReader streamReader = new StreamReader(stream);
string reponse = streamReader.ReadToEnd();
stream.Close();
streamReader.Close();
webResponse.Close();
//ErrorGridHeight = new GridLength(0.3, GridUnitType.Star);
ErrorMsg = msgSucceed;
}
catch (WebException e)
{
if (e.Response != null)
{
aResp = e.Response as HttpWebResponse;
}
}
if (aResp != null)
await errorFunc();
}
private async Task errorFunc()
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => ErrorMsg = msgFailed);
}
I have an error telling me that I can only use the await operator inside an async method who return a Task. I have no idea how it works.
EDIT:
Ok so I changed my code a little bit to avoid the problem of this function:
requete.BeginGetResponse(FinReponse, requete);
because she want a function with a specific prototype so I did this:
private async Task lol(IAsyncResult resultatAsynchrone)
{
HttpWebResponse aResp = null;
try
{
HttpWebRequest requete = (HttpWebRequest)resultatAsynchrone.AsyncState;
WebResponse webResponse = requete.EndGetResponse(resultatAsynchrone);
Stream stream = webResponse.GetResponseStream();
StreamReader streamReader = new StreamReader(stream);
string reponse = streamReader.ReadToEnd();
stream.Close();
streamReader.Close();
webResponse.Close();
//ErrorGridHeight = new GridLength(0.3, GridUnitType.Star);
ErrorMsg = msgSucceed;
}
catch (WebException e)
{
if (e.Response != null)
{
aResp = e.Response as HttpWebResponse;
}
}
if (aResp != null)
await errorFunc();
}
private void FinReponse(IAsyncResult resultatAsynchrone)
{
Task.Run(() => lol(resultatAsynchrone));
}
private async Task errorFunc()
{
await CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => ErrorMsg = msgFailed);
}
The only problem is that it does not change the proprety, it does not go into the set of my proprety. And I have an exeption which is:
System.NotImplementedExeption
Thank you for the help.
Try to change your errorFunc() to this.
private void errorFunc()
{
CoreApplication.MainView.CoreWindow.Dispatcher.BeginInvoke(() =>
{
ErrorMsg = msgFailed;
});
}
I also found another place, where you're trying to change ErrorMsg witouth calling the UI thread. It's the line with code ErrorMsg = msgSucceed;. I suggest to use the same way, so replace the line with this:
CoreApplication.MainView.CoreWindow.Dispatcher.BeginInvoke(() =>
{
ErrorMsg = msgSucceed;
});
You have to do FinReponse method async to be able to use await operator into it:
private async void FinReponse(IAsyncResult resultatAsynchrone)
So I found the answer to access the UI, I have to use:
Deployment.Current.Dispatcher.BeginInvoke(()=>mym());
and it works perfectly.
Thanks for everythings.

Webclient UploadStringCompleted event not being called

I'm writing unit tests for some of the web services we've developed. I have a [TestMethod] that posts to a webservice as rest. Works great however it doesn't trigger the eventhandler method that I created. Through debugging, I've noticed that the eventhandler is getting excluder after the testmethod is executed. It goes to testcleanup.
Has anyone encountered this problem? Here's the code
[TestMethod,TestCategory("WebServices")]
public void ValidateWebServiceGetUserAuthToken()
{
string _jsonstringparams =
"{ \"Password\": \"xxx\", \"UserId\": \"xxxx\"}";
using (var _requestclient = new WebClient())
{
_requestclient.UploadStringCompleted += _requestclient_UploadStringCompleted;
var _uri = String.Format("{0}?format=Json", _webservicesurl);
_requestclient.Headers.Add(HttpRequestHeader.ContentType, "application/json");
_requestclient.UploadStringAsync(new Uri(_uri), "POST", _jsonstringparams);
}
}
void _requestclient_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
{
if (e.Result != null)
{
var _responsecontent = e.Result.ToString();
Console.WriteLine(_responsecontent);
}
else
{
Assert.IsNotNull(e.Error.Message, "Test Case Failed");
}
}
The problem is is that UploadStringAsync returns void (i.e. it's fire and forget) and doesn't inherently let you detect completion.
There's a couple of options. The first option (which is the one I'd recommend) is to use HttpClient instead and use the PostAsync method--which you can await. In which case, I'd do something like this:
[TestMethod, TestCategory("WebServices")]
public async Task ValidateWebServiceGetUserAuthToken()
{
string _jsonstringparams =
"{ \"Password\": \"xxx\", \"UserId\": \"xxxx\"}";
using (var httpClient = new HttpClient())
{
var _uri = String.Format("{0}?format=Json", _webservicesurl);
var stringContent = new StringContent(_jsonstringparams);
stringContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage response = await httpClient.PostAsync(_uri, stringContent);
// Or whatever status code this service response with
Assert.AreEqual(HttpStatusCode.Accepted, response.StatusCode);
var responseText = await response.Content.ReadAsStringAsync();
// TODO: something more specific to your needs
Assert.IsTrue(!string.IsNullOrWhiteSpace(responseText));
}
}
The other option is to change your complete event handler to signal back to your test that the upload is completed and in your test, wait for the event to occur. For example:
[TestMethod, TestCategory("WebServices")]
public void ValidateWebServiceGetUserAuthToken()
{
string _jsonstringparams =
"{ \"Password\": \"xxx\", \"UserId\": \"xxxx\"}";
using (var _requestclient = new WebClient())
{
_requestclient.UploadStringCompleted += _requestclient_UploadStringCompleted;
var _uri = String.Format("{0}?format=Json", _webservicesurl);
_requestclient.Headers.Add(HttpRequestHeader.ContentType, "application/json");
_requestclient.UploadStringAsync(new Uri(_uri), "POST", _jsonstringparams);
completedEvent.WaitOne();
}
}
private ManualResetEvent completedEvent = new ManualResetEvent(false);
void _requestclient_UploadStringCompleted(object sender, UploadStringCompletedEventArgs e)
{
if (e.Result != null)
{
var _responsecontent = e.Result.ToString();
Console.WriteLine(_responsecontent);
}
else
{
Assert.IsNotNull(e.Error.Message, "Test Case Failed");
}
completedEvent.Set();
}
Besides the answers posts above, I've also added a method to support httpwebresponse so I don't have to wait for the event.
public static AuthTokenResponse GetUserToken(string username, string password)
{
string _jsonstringparams =
String.Format("{{ \"Password\": \"{0}\", \"UserId\": \"{1}\"}}", password, username);
string _webservicesurl = ConfigurationManager.AppSettings["WebservicesUrl"];
HttpWebRequest _requestclient = (HttpWebRequest)WebRequest.Create(String.Format("{0}?format=Json", _webservicesurl));
_requestclient.ContentType = "application/json";
_requestclient.Method = "POST";
using (var streamWriter = new StreamWriter(_requestclient.GetRequestStream()))
{
streamWriter.Write(_jsonstringparams);
streamWriter.Flush();
streamWriter.Close();
var httpResponse = (HttpWebResponse)_requestclient.GetResponse();
using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
{
_responsecontent = streamReader.ReadToEnd();
}
AuthTokenResponse _clienttoken = JsonConvert.DeserializeObject<AuthTokenResponse>(_responsecontent);
return _clienttoken;
}
}

GetData from server Windows 8

I have next function:
private void getAllData()
{
HttpWebRequest request = HttpWebRequest.CreateHttp("http://webservice.com/wfwe");
request.BeginGetResponse(new AsyncCallback(GetResponsetStreamCallback), request);
}
void GetResponsetStreamCallback(IAsyncResult callbackResult)
{
HttpWebRequest request = (HttpWebRequest)callbackResult.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(callbackResult);
using (StreamReader httpWebStreamReader = new StreamReader(response.GetResponseStream()))
{
string result = httpWebStreamReader.ReadToEnd();
GetApplications(result);
}
}
And i fill in stack panels:
private void GetApplications(string result)
{
var ApplicationsList = JsonConvert.DeserializeObject<List<Applications>>(result);
foreach (Applications A in ApplicationsList)
{
foreach (ApplicationRelation SCA in A.ApplicationRelations)
{
if (SCA.ApplicationSubcategory != null)
{
#region Fill Customer Research Stack
if (SCA.ApplicationSubcategory.subcategoryName == "Customer Research")
{
if (TestStack.Children.Count == 0)
{
ApplicationTile AT = FillDataForApplicationTile(SCA);
AT.Margin = new Thickness(5, 0, 5, 0);
TestStack.Children.Add(AT);
}
}
#endregion
}
}
}
}
And code fails at:
if (TestStack.Children.Count == 0)
Error: The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))
How can i rewrite my Request from void to string, so i could do something like this:
GetApplications(await getAllData())
EDIT 2 for dcastro:
EDIT 3:
Thanks it works, but i was looking for something like this:
//Modified your code:
GetApplications(getAllData2().Result);
private async Task<string> getAllData2()
{
string uri = "http://webservice.com/wfe";
var client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(uri);
var result = await response.Content.ReadAsStringAsync();
return result.ToString();
}
But somehow my construction doesn't enter GetApplication function...
Instead of using AsyncCallback (which I'm pretty sure is running GetResponsetStreamCallback in a non-UI thread), try fetching your data like this:
private async void getAllData()
string uri = "http://webservice.com/wfwe";
var client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(uri);
string body = await response.Content.ReadAsStringAsync();
GetApplications(body);
}
This will call your webservice asynchronously (at line await Client.sendMessageAsync(msg);), and return to the original UI thread when the response is received. This way, you can update UI elements, like your TestStack.
Edit fixed bug
Try this.
private async Task<string> getAllData()
{
string Result = "";
var http = new HttpClient();
var response = await http.GetAsync("http://webservice.com/wfwe"); // I am considering this URL gives me JSON
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
Result = await response.Content.ReadAsStringAsync(); // You will get JSON here
}
else
{
Result = response.StatusCode.ToString(); // Error while accesing the web service.
}
return Result;
}
private async Task GetApplications(string result)
{
var ApplicationsList = JsonConvert.DeserializeObject<List<Applications>>(result);
foreach (Applications A in ApplicationsList)
{
foreach (ApplicationRelation SCA in A.ApplicationRelations)
{
if (SCA.ApplicationSubcategory != null)
{
#region Fill Customer Research Stack
if (SCA.ApplicationSubcategory.subcategoryName == "Customer Research")
{
if (TestStack.Children.Count == 0)
{
// This will update your UI using UI thread
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () =>
{
ApplicationTile AT = FillDataForApplicationTile(SCA);
AT.Margin = new Thickness(5, 0, 5, 0);
TestStack.Children.Add(AT);
});
}
}
#endregion
}
}
}
}

How to use HttpWebRequest (.NET) asynchronously?

How can I use HttpWebRequest (.NET, C#) asynchronously?
Use HttpWebRequest.BeginGetResponse()
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
The callback function is called when the asynchronous operation is complete. You need to at least call EndGetResponse() from this function.
By far the easiest way is by using TaskFactory.FromAsync from the TPL. It's literally a couple of lines of code when used in conjunction with the new async/await keywords:
var request = WebRequest.Create("http://www.stackoverflow.com");
var response = (HttpWebResponse) await Task.Factory
.FromAsync<WebResponse>(request.BeginGetResponse,
request.EndGetResponse,
null);
Debug.Assert(response.StatusCode == HttpStatusCode.OK);
If you can't use the C#5 compiler then the above can be accomplished using the Task.ContinueWith method:
Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse,
request.EndGetResponse,
null)
.ContinueWith(task =>
{
var response = (HttpWebResponse) task.Result;
Debug.Assert(response.StatusCode == HttpStatusCode.OK);
});
Considering the answer:
HttpWebRequest webRequest;
void StartWebRequest()
{
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), null);
}
void FinishWebRequest(IAsyncResult result)
{
webRequest.EndGetResponse(result);
}
You could send the request pointer or any other object like this:
void StartWebRequest()
{
HttpWebRequest webRequest = ...;
webRequest.BeginGetResponse(new AsyncCallback(FinishWebRequest), webRequest);
}
void FinishWebRequest(IAsyncResult result)
{
HttpWebResponse response = (result.AsyncState as HttpWebRequest).EndGetResponse(result) as HttpWebResponse;
}
Greetings
Everyone so far has been wrong, because BeginGetResponse() does some work on the current thread. From the documentation:
The BeginGetResponse method requires some synchronous setup tasks to
complete (DNS resolution, proxy detection, and TCP socket connection,
for example) before this method becomes asynchronous. As a result,
this method should never be called on a user interface (UI) thread
because it might take considerable time (up to several minutes
depending on network settings) to complete the initial synchronous
setup tasks before an exception for an error is thrown or the method
succeeds.
So to do this right:
void DoWithResponse(HttpWebRequest request, Action<HttpWebResponse> responseAction)
{
Action wrapperAction = () =>
{
request.BeginGetResponse(new AsyncCallback((iar) =>
{
var response = (HttpWebResponse)((HttpWebRequest)iar.AsyncState).EndGetResponse(iar);
responseAction(response);
}), request);
};
wrapperAction.BeginInvoke(new AsyncCallback((iar) =>
{
var action = (Action)iar.AsyncState;
action.EndInvoke(iar);
}), wrapperAction);
}
You can then do what you need to with the response. For example:
HttpWebRequest request;
// init your request...then:
DoWithResponse(request, (response) => {
var body = new StreamReader(response.GetResponseStream()).ReadToEnd();
Console.Write(body);
});
public static async Task<byte[]> GetBytesAsync(string url) {
var request = (HttpWebRequest)WebRequest.Create(url);
using (var response = await request.GetResponseAsync())
using (var content = new MemoryStream())
using (var responseStream = response.GetResponseStream()) {
await responseStream.CopyToAsync(content);
return content.ToArray();
}
}
public static async Task<string> GetStringAsync(string url) {
var bytes = await GetBytesAsync(url);
return Encoding.UTF8.GetString(bytes, 0, bytes.Length);
}
I ended up using BackgroundWorker, it is definitely asynchronous unlike some of the above solutions, it handles returning to the GUI thread for you, and it is very easy to understand.
It is also very easy to handle exceptions, as they end up in the RunWorkerCompleted method, but make sure you read this: Unhandled exceptions in BackgroundWorker
I used WebClient but obviously you could use HttpWebRequest.GetResponse if you wanted.
var worker = new BackgroundWorker();
worker.DoWork += (sender, args) => {
args.Result = new WebClient().DownloadString(settings.test_url);
};
worker.RunWorkerCompleted += (sender, e) => {
if (e.Error != null) {
connectivityLabel.Text = "Error: " + e.Error.Message;
} else {
connectivityLabel.Text = "Connectivity OK";
Log.d("result:" + e.Result);
}
};
connectivityLabel.Text = "Testing Connectivity";
worker.RunWorkerAsync();
.NET has changed since many of these answers were posted, and I'd like to provide a more up-to-date answer. Use an async method to start a Task that will run on a background thread:
private async Task<String> MakeRequestAsync(String url)
{
String responseText = await Task.Run(() =>
{
try
{
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
WebResponse response = request.GetResponse();
Stream responseStream = response.GetResponseStream();
return new StreamReader(responseStream).ReadToEnd();
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.Message);
}
return null;
});
return responseText;
}
To use the async method:
String response = await MakeRequestAsync("http://example.com/");
Update:
This solution does not work for UWP apps which use WebRequest.GetResponseAsync() instead of WebRequest.GetResponse(), and it does not call the Dispose() methods where appropriate. #dragansr has a good alternative solution that addresses these issues.
public void GetResponseAsync (HttpWebRequest request, Action<HttpWebResponse> gotResponse)
{
if (request != null) {
request.BeginGetRequestStream ((r) => {
try { // there's a try/catch here because execution path is different from invokation one, exception here may cause a crash
HttpWebResponse response = request.EndGetResponse (r);
if (gotResponse != null)
gotResponse (response);
} catch (Exception x) {
Console.WriteLine ("Unable to get response for '" + request.RequestUri + "' Err: " + x);
}
}, null);
}
}
Follow up to the #Isak 's answer, which is very good. Nonetheless it's biggest flaw is that it will only call the responseAction if the response has status 200-299. The best way to fix this is:
private void DoWithResponseAsync(HttpWebRequest request, Action<HttpWebResponse> responseAction)
{
Action wrapperAction = () =>
{
request.BeginGetResponse(new AsyncCallback((iar) =>
{
HttpWebResponse response;
try
{
response = (HttpWebResponse)((HttpWebRequest)iar.AsyncState).EndGetResponse(iar);
}
catch (WebException ex)
{
// It needs to be done like this in order to read responses with error status:
response = ex.Response as HttpWebResponse;
}
responseAction(response);
}), request);
};
wrapperAction.BeginInvoke(new AsyncCallback((iar) =>
{
var action = (Action)iar.AsyncState;
action.EndInvoke(iar);
}), wrapperAction);
}
And then as #Isak follows:
HttpWebRequest request;
// init your request...then:
DoWithResponse(request, (response) => {
var body = new StreamReader(response.GetResponseStream()).ReadToEnd();
Console.Write(body);
});
I've been using this for async UWR, hopefully it helps someone
string uri = "http://some.place.online";
using (UnityWebRequest uwr = UnityWebRequest.Get(uri))
{
var asyncOp = uwr.SendWebRequest();
while (asyncOp.isDone == false) await Task.Delay(1000 / 30); // 30 hertz
if(uwr.result == UnityWebRequest.Result.Success) return uwr.downloadHandler.text;
Debug.LogError(uwr.error);
}

Categories

Resources