简体   繁体   中英

Chaining tasks of varying return types (TPL and HttpWebRequest)

Folks, I'm finding myself in a weird situation. I need to chain tasks of different returning types together. Normally, you can do something like this

Task<T> t = Task<T>.Factory.StartNew(() => ... some T instance);
Task t2 = t.ContinueWith<U>(parent => ...);
return Task.WhenAll(t, t2);

My complication however, lies in using the FromAsync helper method to wrap a Begin/End pair and convert that into a Task. I'm trying to write an asynchronous client using HttpWebRequest and tasks on .NET 4.0 (so await is not an option).

My problem is that the return type of FromAsync is a Task itself which prevents me from using it in a ContinueWith method ( ContinueWith expects the return type and wraps the data itself in a Task object).

Here is the code I have so far, which yields the correct functional result, but is not truly asynchronous:

public Task<string> GetHttpRequest(string url, string contentType)
{
    var httpWebRequest = CreateHttpWebRequest(url, "GET", contentType);
    Task<WebResponse> httpTask = Task.Factory.FromAsync<WebResponse>(httpWebRequest.BeginGetResponse, httpWebRequest.EndGetResponse, null);

    return httpTask.ContinueWith(httpAntecedent =>
          {
              WebResponse webResponse = httpAntecedent.Result;
              Stream responseStream = webResponse.GetResponseStream();
              byte[] data = new byte[webResponse.ContentLength];

              var streamReadTask = Task<int>.Factory.FromAsync(responseStream.BeginRead, responseStream.EndRead, data, 0, data.Length, TaskCreationOptions.AttachedToParent);

              return streamReadTask.ContinueWith(parent =>
                  {
                      responseStream.Close();
                      webResponse.Close();

                      return Encoding.UTF8.GetString(data);
                  });
          }).Result;
}

To rephrase your question, you have Task<Task<string>> and you want to get Task<string> from that, without synchronously waiting for the Task to complete.

In C# 5.0, you could do this by using double await : return await await task; .

Without C# 5.0, you can use Unwrap() , it does exactly what you want: return task.Unwrap(); .

If, for some reason, you wanted to do this by yourself, you could use ContinueWith() inside ContinueWith() together with TaskCompletionSource .


But your code is flawed: it assumes that you will get the whole response in a single read. That's not guaranteed at all, and in fact won't work correctly quite often. Doing this properly would require more complicated code and probably also TaskCompletionSource .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM