I have some code that lays out like this:
Class1
Task<List<ConfSession>> getSessionsTask = Task.Factory.StartNew(() =>
{
var confSessions =
TaskHelper<ConfSession>.InvokeTaskMagic(request);
//PROBLEM - THIS CODE RACES TO THE NEXT LINE
//BEFORE confSessions IS POPULATED FROM THE LINE ABOVE - IE
//confSessions IS ALWAYS AN EMPTY LIST
//WHEN IT'S RETURNED
return confSessions;
}
);
Class2 (TaskHelper)
//methods
public static List<T> InvokeTaskMagic(HttpWebRequest request)
{
var resultList = new List<T>();
Task<WebResponse> task1 = Task<WebResponse>.Factory.FromAsync(
(callback, o) => ((HttpWebRequest)o).BeginGetResponse(callback, o)
, result => ((HttpWebRequest)result.AsyncState).EndGetResponse(result)
, request);
task1.ContinueWith((antecedent) =>
{
if (antecedent.IsFaulted)
{
return;
}
WebResponse webResponse;
try
{
webResponse = task1.Result;
}
catch (AggregateException ex1)
{
throw ex1.InnerException;
}
string responseString;
using (var response = (HttpWebResponse)webResponse)
{
using (Stream streamResponse = response.GetResponseStream())
{
StreamReader reader = new StreamReader(streamResponse);
responseString = reader.ReadToEnd();
reader.Close();
}
}
if (responseString != null)
{
resultList =
JsonConvert.DeserializeObject<List<T>>(responseString);
}
});
return resultList;
}
TaskHelper is a class I created to standardize a bunch of redundant task code that I had in several methods. It exists only to take an HttpWebRequest, execute it in a Task, get the response in the ContinueWith block, parse the response into a List<T>
, and return the List<T>
.
I wrapped Class1's call to TaskHelper in a task because I need to get the result from the InvokeTaskMagic method before I continue in class1 (i.e. the next step in class1 is to use the List<T>
. As it says in the comments in the code, my problem is that the Task in class1 returns an empty list every time because it's not waiting for the response from the InvokeTaskMagic method in the TaskHelper class.
Is this expected? Is there a way to make getSessionsTask wait to return until TaskHelper.InvokeTaskMagic returns?
UPDATE: The working code follows - thanks Servy for your help.
public static class TaskHelper<T> where T : class
{
//methods
public static Task<List<T>> InvokeTaskMagic(HttpWebRequest request)
{
var resultList = new List<T>();
Task<WebResponse> task1 = Task<WebResponse>.Factory.FromAsync(
(callback, o) => ((HttpWebRequest)o).BeginGetResponse(callback, o)
, result => ((HttpWebRequest)result.AsyncState).EndGetResponse(result)
, request);
return task1.ContinueWith<List<T>>((antecedent) =>
{
if (antecedent.IsFaulted)
{
return new List<T>();
}
WebResponse webResponse;
try
{
webResponse = task1.Result;
}
catch (AggregateException ex1)
{
throw ex1.InnerException;
}
string responseString;
using (var response = (HttpWebResponse)webResponse)
{
using (Stream streamResponse = response.GetResponseStream())
{
StreamReader reader = new StreamReader(streamResponse);
responseString = reader.ReadToEnd();
reader.Close();
}
}
if (responseString != null)
{
return JsonConvert.DeserializeObject<List<T>>(responseString);
}
else
{
return new List<T>();
}
});
}
}
The InvokeTaskMagic method is called like this:
var getCommentsAboutTask = Task.Factory.StartNew(() =>
{
var comments = TaskHelper<Comment>.InvokeTaskMagic(request);
return comments;
});
getCommentsAboutTask.ContinueWith((antecedent) =>
{
if (antecedent.IsFaulted)
{ return; }
var commentList = antecedent.Result.Result;
UIThread.Invoke(() =>
{
foreach (Comment c in commentList)
{
AllComments.Add(c);
GetCommentRelatedInfo(c);
}
});
});
Is this expected? Is there a way to make my class1.task1 continueblock wait for my class2.task2 continueblock?
Sure, just call ContinueWith
on the second task's continuation instead of on the first task.
If you need it to wait until both are done you can use Task.Factory.ContinueWhenAll
.
I have done something similar to your code some time ago and it is difficult getting it to work properly with error handling etc.
First I created a generic method to handles continuations with different return type
private AsyncCallback EndAsync<T1, T2>(TaskCompletionSource<T2> tcs,
Func<IAsyncResult, T1> endMethod,
Func<T1, T2> continueWith) {
return ar => {
T1 result1;
try {
result1 = endMethod(ar);
}
catch (Exception err) {
tcs.SetException(err);
return;
}
try {
T2 result2 = continueWith(result1);
tcs.SetResult(result2);
}
catch (Exception err) {
tcs.SetException(err);
return;
}
};
}
Then I create the async task like this:
public Task<List<T>> GetDataAsync(IProxy proxy) {
var tcs = new TaskCompletionSource<List<T>>();
var asyncCallback = EndAsync(tcs,
proxy.EndGetData,
result => result != null ?
ProcessResult(result) :
new List<T>());
proxy.BeginGetData(asyncCallback);
return tcs.Task;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With