添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
相关文章推荐
英勇无比的爆米花  ·  AnalyticDB for ...·  7 月前    · 
火星上的烤地瓜  ·  python - Google Colab ...·  12 月前    · 
会开车的紫菜  ·  “‘Get ...·  1 年前    · 
阳刚的青蛙  ·  K8S ...·  1 年前    · 
Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

Should I await ReadAsStringAsync() if I awaited the response that I'm performing ReadAsStringAsync() on?

Ask Question

Should I await ReadAsStringAsync() if I awaited the response on which I'm performing ReadAsStringAsync() ? To clarify further, what is the difference or the right way between the following? Are they effectively the same?

var response = await httpClient.GetAsync("something");
var content = await response.Content.ReadAsStringAsync();
return new AvailableViewingTimesMapper().Map(content);
var response = await httpClient.GetAsync("something");
var content = response.Content.ReadAsStringAsync();
return new AvailableViewingTimesMapper().Map(content.Result);
                I think you would only do var content = response.Content.ReadAsStringAsync(); if you wanted to do something with the task before you use the result.
– rory.ap
                Jan 28, 2015 at 22:06
                You should never do Map(content.Result) you can deadlock your program, if you are going to defer it you need to still use await. Map(await content),
– Scott Chamberlain
                Jan 28, 2015 at 22:22

Your first example is the correct one. The second example does not yield during the asynchronous operation. Instead, by getting the value of the content.Result property, you force the current thread to wait until the asynchronous operation has completed.

In addition, as commenter Scott Chamberlain points out, by blocking the current thread it is possible you could introduce the possibility of deadlock. That depends on the context, but a common scenario for await is to use that statement in the UI thread, and the UI thread needs to remain responsive for a variety of needs, but including to be able to actually handle the completion of an awaited operation.

If you avoid the second pattern, i.e. retrieving the value of the Result property from a Task you don't know has completed, not only can you ensure efficient use of your threads, you can also ensure against this common deadlock trap.

As a extension of the last paragraph, there is no extra cost to await on a already completed Task, it will just synchronously return the already available result. If you are in a method that is already marked async there is no reason to ever call .Result over await on a task. – Scott Chamberlain Jan 29, 2015 at 6:11 I would like to see the other comment given here by @ScottChamberlain added to the answer because it's probably the best info for what the OP was getting at. Their example caused confusion about blocking with Result (hence the bulk of this answer) but they still needed to await the method on the Response they already awaited. You shouldn't worry about awaiting an awaitable; the worst that can come of it is you immediately get your results which sounds like a positive. – ChiefTwoPencils Aug 8, 2019 at 1:22

The reason why ReadAsString is an async method is, that actually reading the data is an IO Operation. The content might just not be fully loaded even if you already have the http result. There are no additional threads or big loads of computing involved.

HttpClient.GetAsync allows you to add a HttpCompletionOption to have the GetAsync only return once the whole HttpResult has been loaded. In that case, HttpContent.ReadAsStringAsync will complete synchronously (a so called fastpath) because the content is already there.

So you should definitely await it.

Also: As this is probably library code that does not depend on returning at the UI thread, you should add .ConfigureAwait(false) to all awaited method calls.

"As this is library code" -- how do you know this is library code? I seem to have missed that part in the question. The code fragments seem like they could just as easily be part of some UI code, where ConfigureAwait(false) would be exactly the wrong thing to do. – Peter Duniho Jan 29, 2015 at 6:07 @PeterDuniho but we do see all of the code between the first await and the return statement, there appears to be no UI calls there so this would be a good candidate for ConfigureAwait(false) – Scott Chamberlain Jan 29, 2015 at 6:13 @ScottChamberlain: probably, but without context it's hard to be sure. For all we know, the OP simply elided the UI-related stuff. My concern would be that, adding ConfigureAwait(false) to code where it shouldn't be present is much worse than not having it in code where it should be present. I.e. the latter is simply an optimization (for code that is otherwise correct), while the former would break something. – Peter Duniho Jan 29, 2015 at 6:19 @roryap: ConfigureAwait(false) tells the task that it does not need to run the continuation in the current context. This is fine for anything not related to UI code; but if the continuation includes code that requires execution in the current context (i.e. the UI thread), such as accessing some UI object, calling ConfigureAwait(false) will cause the continuation to be run on a thread other than the UI thread, with the result that when the UI object is accessed, an InvalidOperationException (or similar...that's Winforms, I don't recall WPF's off the top of my head) will be thrown. – Peter Duniho Jan 29, 2015 at 17:52 @roryap: using Invoke() would get around the problem, but would also pretty much negate the whole point of using await in that scenario. – Peter Duniho Jan 29, 2015 at 18:13

Here is .NET source code for ReadAsStringAsync. If you look deeper in LoadIntoBufferAsync() method, you will see this will keep reading buffer from HttpResponse and leads to a potential further network call. This means it is a good practice to use await instead of Result.

[__DynamicallyInvokable]
    public Task<string> ReadAsStringAsync()
      this.CheckDisposed();
      TaskCompletionSource<string> tcs = new TaskCompletionSource<string>();
      HttpUtilities.ContinueWithStandard(this.LoadIntoBufferAsync(), (Action<Task>) (task =>
        if (HttpUtilities.HandleFaultsAndCancelation<string>(task, tcs))
          return;
        if (this.bufferedContent.Length == 0L)
          tcs.TrySetResult(string.Empty);
          Encoding encoding1 = (Encoding) null;
          int index = -1;
          byte[] buffer = this.bufferedContent.GetBuffer();
          int dataLength = (int) this.bufferedContent.Length;
          if (this.Headers.ContentType != null)
            if (this.Headers.ContentType.CharSet != null)
                encoding1 = Encoding.GetEncoding(this.Headers.ContentType.CharSet);
              catch (ArgumentException ex)
                tcs.TrySetException((Exception) new InvalidOperationException(SR.net_http_content_invalid_charset, (Exception) ex));
                return;
          if (encoding1 == null)
            foreach (Encoding encoding2 in HttpContent.EncodingsWithBom)
              byte[] preamble = encoding2.GetPreamble();
              if (HttpContent.ByteArrayHasPrefix(buffer, dataLength, preamble))
                encoding1 = encoding2;
                index = preamble.Length;
                break;
          Encoding encoding3 = encoding1 ?? HttpContent.DefaultStringEncoding;
          if (index == -1)
            byte[] preamble = encoding3.GetPreamble();
            index = !HttpContent.ByteArrayHasPrefix(buffer, dataLength, preamble) ? 0 : preamble.Length;
            tcs.TrySetResult(encoding3.GetString(buffer, index, dataLength - index));
          catch (Exception ex)
            tcs.TrySetException(ex);
      return tcs.Task;
        

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.