I had a relatively painful head-pounding issue the last few days.
While executing unit tests intended to verify async behavior in Visual Studio 2017, the async method calls appear to be blocked, but ONLY when I made calls to a web API (I promise, I had locally mocked services first, but then needed some integration tests with an API to prove the web requests behaved similarly).
I finally rang a friend to help me think this through, to which he re-wrote the same tests and showed his tests behaved as expected. The only differentiating factor was that he wrote his tests in VS2017 with .net Core 2.0.
Example Code:
[TestMethod]
public void TestRestSharpExecuteTaskAsync()
{
var tasks = new List<Task>();
Task.WhenAll(Enumerable.Range(1, 10).Select(async s =>
{
// Using RestSharp, but also tested using raw WebRequests
var restClient = new RestClient("http://google.com");
var request = new RestRequest("/", RestSharp.Method.GET);
Debug.WriteLine(string.Format("{0} | DEBUG: BEGIN-ExecuteTaskAsync()", DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.fff")));
var stopwatch = new Stopwatch();
stopwatch.Start();
var response = await restClient.ExecuteTaskAsync(request);
stopwatch.Stop();
Debug.WriteLine(string.Format("{0} | DEBUG: END-ExecuteTaskAsync() completed in {1} milliseconds", DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss.fff"), stopwatch.ElapsedMilliseconds));
})).Wait();
}
What is currently confusing me, is that when this code is executed in VS2017 with .net 4.5+ (also tested 4.6.2), the results show that there are clearly tasks waiting:
11/30/2017 10:45:45.214 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.253 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.253 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.253 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.253 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.254 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.254 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.254 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.254 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.254 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:45.368 | DEBUG: END-ExecuteTaskAsync() completed in 151 milliseconds
11/30/2017 10:45:45.368 | DEBUG: END-ExecuteTaskAsync() completed in 115 milliseconds
11/30/2017 10:45:45.414 | DEBUG: END-ExecuteTaskAsync() completed in 161 milliseconds
11/30/2017 10:45:45.420 | DEBUG: END-ExecuteTaskAsync() completed in 166 milliseconds
11/30/2017 10:45:45.466 | DEBUG: END-ExecuteTaskAsync() completed in 212 milliseconds
11/30/2017 10:45:45.473 | DEBUG: END-ExecuteTaskAsync() completed in 218 milliseconds
11/30/2017 10:45:45.520 | DEBUG: END-ExecuteTaskAsync() completed in 266 milliseconds
11/30/2017 10:45:45.522 | DEBUG: END-ExecuteTaskAsync() completed in 268 milliseconds
11/30/2017 10:45:45.570 | DEBUG: END-ExecuteTaskAsync() completed in 315 milliseconds
11/30/2017 10:45:45.578 | DEBUG: END-ExecuteTaskAsync() completed in 323 milliseconds
However, when run in the same VS2017 IDE with a .net Core 2.0 project, the tasks execute asynchronously as expected:
11/30/2017 10:45:11.922 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.976 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.976 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:11.977 | DEBUG: BEGIN-ExecuteTaskAsync()
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 230 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 176 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 175 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 175 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 175 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 176 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 176 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 175 milliseconds
11/30/2017 10:45:12.153 | DEBUG: END-ExecuteTaskAsync() completed in 176 milliseconds
11/30/2017 10:45:12.154 | DEBUG: END-ExecuteTaskAsync() completed in 177 milliseconds
Is this an IDE configuration? A bug in .net 4.x? What gives?
EDIT: The issue becomes more visible when I hit an endpoint I control the response time with:
In VS2017, .net Core 2.0:
12/01/2017 12:03:29.725 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.770 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.771 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:29.772 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4233 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4232 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4233 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4279 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4233 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4233 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4233 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4233 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4234 milliseconds
12/01/2017 12:03:34.005 | DEBUG: END-ExecuteTaskAsync() completed in 4235 milliseconds
In VS2017, .net 4.5:
12/01/2017 12:03:49.972 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.010 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:50.011 | DEBUG: BEGIN-ExecuteTaskAsync()
12/01/2017 12:03:54.132 | DEBUG: END-ExecuteTaskAsync() completed in 4158 milliseconds
12/01/2017 12:03:54.132 | DEBUG: END-ExecuteTaskAsync() completed in 4121 milliseconds
12/01/2017 12:03:58.189 | DEBUG: END-ExecuteTaskAsync() completed in 8178 milliseconds
12/01/2017 12:03:58.191 | DEBUG: END-ExecuteTaskAsync() completed in 8180 milliseconds
12/01/2017 12:04:02.236 | DEBUG: END-ExecuteTaskAsync() completed in 12225 milliseconds
12/01/2017 12:04:02.239 | DEBUG: END-ExecuteTaskAsync() completed in 12228 milliseconds
12/01/2017 12:04:06.300 | DEBUG: END-ExecuteTaskAsync() completed in 16288 milliseconds
12/01/2017 12:04:06.302 | DEBUG: END-ExecuteTaskAsync() completed in 16290 milliseconds
12/01/2017 12:04:10.362 | DEBUG: END-ExecuteTaskAsync() completed in 20350 milliseconds
12/01/2017 12:04:10.364 | DEBUG: END-ExecuteTaskAsync() completed in 20352 milliseconds
Again, the IDE is the same, the code is identical. The applied difference being the .net version.