3

Im trying to rewrite some of my old code using async/await and chaining Tasks using ContinueWith() and checking for exceptions with TaskContinuationOptions.NotOnFaulted.

When I debug the code I noticed that it does not run as I expected. Both webrequests are successful but only the first continuation processes the response.

The second continuation does not complete and the last one gives me the result:

Id = 1, Status = RanToCompletion, Method = "{null}", Result = "System.Threading.Tasks.Task`1[System.Threading.Tasks.VoidTaskResult]"

and the result:

Id = 2, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}"

My question is what am I doing wrong and what can I do so the second continuation completes. I am also interested if it is considered good practice to chain tasks together using ContinueWith or if there is a better way to do it without writing a bunch of clumsy methods? Thanks for the help

   using Newtonsoft.Json.Linq;                  

   var api = new Api();
   var order = new Dictionary<string, object>();
   await api.MakeRequest(Api.Endpoint.Orders, HttpMethod.Get, null, "?completed=false&page=" + count)
   //Look for new Orders
   .ContinueWith(ant =>
    {
          dynamic jsonOrder = JObject.Parse(ant.Result);
          JArray data = jsonOrder.data;
          //Process Json Response
          order.Add("customer_name", (string)data[j]["customer_name"]);
          order.Add("product_id", (string)data[j]["product_id"]);
          order.Add("order_id", (string)data[j]["order_id"]);
          order.Add("timestamp", (int)data[j]["timestamp"]);
          //Entries are successfully added
    }, TaskContinuationOptions.NotOnFaulted )
    //Now get more details about the product
    .ContinueWith(async (ant) =>
    {
          string result = await api.MakeRequest(Api.Endpoint.Product, HttpMethod.Get, null, (string)order["product_id"]); 
          //The Request succeeds 

          //This code block does not execute
          dynamic json = JObject.Parse(result);
          order.Add("deadline", (int)json.data.deadline);
          order.Add("price", (string)json.data.price);
          order.Add("amount", (int)json.data.amount);
          //This code block does not execute 

     }, TaskContinuationOptions.NotOnFaulted)
     //Get some more details about the Customer (isRecurring? etc)
     .ContinueWith(async (ant) =>
     {
        //Some more code here
     }
Timo1995
  • 137
  • 1
  • 3
  • 9
  • 1
    *if there is a better way to do it without writing a bunch of clumsy methods?* Use async/await – Sriram Sakthivel Aug 27 '14 at 09:14
  • 1
    You don't need to ContinueWith a task you are awaiting, that's the whole point of the syntax. Just await the task and then the code after the await will only execute when the task has completed or failed. You extract the result from the value returned by the method you are awaiting e.g. `var result = await api.MakeRequest(...` . You can use standard try/catch syntax for exception handling. – Ben Robinson Aug 27 '14 at 09:24
  • Yes that makes sense. But what if I want to process orders in parallel and not await the task? – Timo1995 Aug 27 '14 at 09:49

1 Answers1

1

Like @Ben Robinson said the use of await automatically registers the rest of the method as a continuation which is only executed if the operation succeeds, otherwise an exception is thrown. I'd change my method to remove the ContinueWith calls and consider using ConfigureAwait(false) if you don't need to return to the current SynchrionizationContext after the asynchronous operations are finished, i.e. the rest of the method will continue execution on the thread pool thread. You may also find this article useful.

var api = new Api();
var order = new Dictionary<string, object>();

await api.MakeRequest(Api.Endpoint.Orders, HttpMethod.Get, null, "?completed=false&page=" + count).ConfiugureAwait(false);

//Look for new Orders
dynamic jsonOrder = JObject.Parse(ant.Result);
JArray data = jsonOrder.data;
//Process Json Response
order.Add("customer_name", (string)data[j]["customer_name"]);
order.Add("product_id", (string)data[j]["product_id"]);
order.Add("order_id", (string)data[j]["order_id"]);
order.Add("timestamp", (int)data[j]["timestamp"]);

//Now get more details about the product
string result = await api.MakeRequest(Api.Endpoint.Product, HttpMethod.Get, null, (string)order["product_id"]).ConfiugureAwait(false);

dynamic json = JObject.Parse(result);
order.Add("deadline", (int)json.data.deadline);
order.Add("price", (string)json.data.price);
order.Add("amount", (int)json.data.amount);
NeddySpaghetti
  • 13,187
  • 5
  • 32
  • 61