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);
}
});
});
Sure, just call
ContinueWithon 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.