I've used async coding a little bit but I don't really fully understand how to use it -- though I understand the concept and why I need it.
Here's my set up:
I have a Web API that I will call from my ASP.NET MVC app and my Web API will call DocumentDB. In code samples, I see a lot of await keywords while sending queries to DocumentDB.
I'm confused if I need to make my Index action method in my MVC app async?
I'm also confused if my CreateEmployee() method in my Web API should be async?
What is the right way to use async in this scenario?
Here's my code (This code is currently giving me errors because my MVC action method is not async)
---- ASP.NET MVC App Code ----
public ActionResult Index()
{
Employee emp = new Employee();
emp.FirstName = "John";
emp.LastName = "Doe";
emp.Gender = "M";
emp.Ssn = "123-45-6789";
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://myWebApi.com");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage response = await client.PostAsJsonAsync("hr/create/newemployee", emp);
if (response.IsSuccessStatusCode)
{
emp = await response.Content.ReadAsAsync<Employee>();
}
}
// Display employee info
return View(emp);
}
---- Web API Code ----
private static readonly string endPointUrl = ConfigurationManager.AppSettings["EndPointUrl"];
private static readonly string authorizationKey = ConfigurationManager.AppSettings["AuthorizationKey"];
private static readonly string databaseId = ConfigurationManager.AppSettings["DatabaseId"];
private static DocumentClient client;
public static async Task<Employee> CreateEmployee(Employee emp)
{
try
{
//Create a Document client
using (client = new DocumentClient(new Uri(endPointUrl), authorizationKey))
{
//Get the database
var database = await GetDatabaseAsync();
//Get the Document Collection
var collection = await GetCollectionAsync(database.SelfLink, "Employees");
await client.CreateDocumentAsync(collection.SelfLink, emp);
// Further process employee
}
}
catch
{
// Handle error
}
return employee;
}
private static async Task<DocumentCollection> GetCollectionAsync(string dbLink, string id)
{
DocumentCollection collection = client.CreateDocumentCollectionQuery(dbLink).Where(c => c.Id == id).ToArray().FirstOrDefault();
return collection;
}
private static async Task<Database> GetDatabaseAsync()
{
Database database = client.CreateDatabaseQuery().Where(db => db.Id == databaseId).ToArray().FirstOrDefault();
return database;
}
Here's my explanation
class MainClass
{
public static async Task<String> AsyncMethod(int delay) {
await Task.Delay (TimeSpan.FromSeconds(delay));
return "The method has finished it's execution after waiting for " + delay + " seconds";
}
public static async Task Approach1(int delay)
{
var response = await AsyncMethod (delay); // await just unwraps Task's result
Console.WriteLine (response);
}
public static Task Approach2(int delay)
{
return AsyncMethod(delay).ContinueWith(message => Console.WriteLine(message)); // you could do the same with
}
public static void Main (string[] args)
{
var operation1 = Approach1 (3);
var operation2 = Approach2 (5);
Task.WaitAll (operation1, operation2);
Console.WriteLine("All operations are completed")
}
}
Eventually both Approach1 and Approach2 are identical pieces of code.
The async/await is syntactic sugar around Task API. It takes your async method splits it into parts before await, and after await. The "before" part is executed immediately. The "after" part is getting executed when await operation is completed. You are able to track the second part of operation via the Task API since you get a reference to a Task.
In general async allows to treat a method call as a some sort of long operation that you can reference via the Task API and wait until it is finished and continue with another piece of code. Either via ContinueWith call of via using await in general it's the same.
Before async/await/Task concepts people were using callbacks, but handling errors was as easy as hell, the Task is similar to a concept of callback except that it is able allow handling exceptions more easily.
In general all this Task/async/await mantra is close to concept of promises if it happen that you've worked with jQuery/JavaScript there's a similar concept here's a nice question explaining how it's done there "jQuery deferreds and promises - .then() vs .done()"
Edit: I've just found out that .NET lacks implementation of then functionality similar to one found in jQuery/JavaScript.
The difference between ContinueWith and Then is that Then is able to compose task, and to execute them sequentially while ContinueWith is not, it is able only to launch task in parallel, but it can be easily implemented via the await construct. Here is my updated code containing the whole shebang:
static class Extensions
{
// Implementation to jQuery-like `then` function in .NET
// According to: http://blogs.msdn.com/b/pfxteam/archive/2012/08/15/implementing-then-with-await.aspx
// Further reading: http://blogs.msdn.com/b/pfxteam/archive/2010/11/21/10094564.aspx
public static async Task Then(this Task task, Func<Task> continuation)
{
await task;
await continuation();
}
public static async Task<TNewResult> Then<TNewResult>(
this Task task, Func<Task<TNewResult>> continuation)
{
await task;
return await continuation();
}
public static async Task Then<TResult>(
this Task<TResult> task, Func<TResult,Task> continuation)
{
await continuation(await task);
}
public static async Task<TNewResult> Then<TResult, TNewResult>(
this Task<TResult> task, Func<TResult, Task<TNewResult>> continuation)
{
return await continuation(await task);
}
}
class MainClass
{
public static async Task<String> AsyncMethod1(int delay) {
await Task.Delay (TimeSpan.FromSeconds(delay));
return "The method has finished it's execution after waiting for " + delay + " seconds";
}
public static Task<String> AsyncMethod2(int delay)
{
return Task.Delay (TimeSpan.FromSeconds (delay)).ContinueWith ((x) => "The method has finished it's execution after waiting for " + delay + " seconds");
}
public static async Task<String> Approach1(int delay)
{
var response = await AsyncMethod1 (delay); // await just unwraps Task's result
return "Here is the result of AsyncMethod1 operation: '" + response + "'";
}
public static Task<String> Approach2(int delay)
{
return AsyncMethod2(delay).ContinueWith(message => "Here is the result of AsyncMethod2 operation: '" + message.Result + "'");
}
public static void Main (string[] args)
{
// You have long running operations that doesn't block current thread
var operation1 = Approach1 (3); // So as soon as the code hits "await" the method will exit and you will have a "operation1" assigned with a task that finishes as soon as delay is finished
var operation2 = Approach2 (5); // The same way you initiate the second long-running operation. The method also returns as soon as it hits "await"
// You can create chains of operations:
var operation3 = operation1.ContinueWith(operation1Task=>Console.WriteLine("Operation 3 has received the following input from operation 1: '" + operation1Task.Result + "'"));
var operation4 = operation2.ContinueWith(operation2Task=>Console.WriteLine("Operation 4 has received the following input from operation 2: '" + operation2Task.Result + "'"));
var operation5 = Task.WhenAll (operation3, operation4)
.Then(()=>Task.Delay (TimeSpan.FromSeconds (7)))
.ContinueWith((task)=>Console.WriteLine("After operation3 and 4 have finished, I've waited for additional seven seconds, then retuned this message"));
Task.WaitAll (operation1, operation2); // This call will block current thread;
operation3.Wait (); // This call will block current thread;
operation4.Wait (); // This call will block current thread;
operation5.Wait (); // This call will block current thread;
Console.WriteLine ("All operations are completed");
}
}
you can only use await inside a method if that method is async and async methods need to return Task, Task<T> or void although void returning async methods are reserved for event handlers because the exceptions thrown within them are swallowed and you cannot await their completion or chain subsequent tasks.
I think your Index action needs to be async and return a Task<ActionResult> and your CreateEmployee method needs to be async as well as it is using await inside it.
See Best Practices in Asynchronous Programming for some guidelines on when and how to use async-await
async await
They are tricky to understand.
First of all, in your methods in Web API, you are using async without await. I'm sure you are getting some errors / warning there right?
--
async await are used to return the working thread back to the caller when you are waiting for I/O to be finished. So, yes, you do want to use it both in your MVC and Web API side. Please make sure you understand this sentence before moving on.
--
The thing about async / await is that, if you use it, you have to use it ALL the way through the calling functions, or else it doesn't make sense (and you'll get errors / warning too). This means that whatever library you are using must support it. In this case "DocumentClient". By convention, the methods that support it will end in "Async" and it will return a Task which you can await.
--
So your short answer:
use async await from the very beginning (your controller), and try to make it await whatever long operations it calls. If that is also your code, you should be able to await from there ... and await from there ... until you finally call something that is not your code. If you can await that code that is not yours, then you are set. If you cannot, then you should not use async await form the very beginning.
(no way this made sense)
Related
This question already has an answer here:
EF6 two contexts vs. single context with two awaits
(1 answer)
Closed 4 years ago.
After going through a lot of articles and videos i am still having problem with the asynchronous programming.I am working on a project where in service layer I have created all the methods as async. All of the return Task <T> or Task (I made sure not to return void).
Now to the issue. My Api calls async methods which internally calls other async methods which may even call other async methods. So I await every time I encounter an async method.
The downside with this approach, I think, is that cause I am awaiting for result every time I encounter async it will take a lot of time. For ex:
public async Task<xyz> DoMainTask(int ActionId, int ItemId, int UserId)
{
await DoTask1(ActionId,ItemId,UserId); 3 sec
await DoTask2(ActionId,ItemId,UserId); 3 sec
await DoTask3(ActionId,ItemId,UserId); 3 sec
}
So I don't want to wait for 9 sec because all tasks here are independent of each other.
I want to do something like:
public async Task<xyz> DoMainTask(int ActionId, int ItemId, int UserId)
{
List<Task> lst = new List<Task>();
t1= DoTask1(ActionId,ItemId,UserId);
lst.Add(t1);
t2 = DoTask2(ActionId,ItemId,UserId);
lst.Add(t2);
t3 = DoTask3(ActionId,ItemId,UserId);
lst.Add(t3);
await Task.WhenAll(lst);
// do some work
return xyz;
}
Which will probably take around 5-6 sec. How do I do this?
Whenever I try to use 2nd approach it get error:
A second operation started on this context before a previous asynchronous operation completed
DoTask1 is defined as:
public async Task DoTask1 (int ActionId, int ItemId, int UserId)
{
try
{
DailyActivityPoint dailyActivityPoint = new DailyActivityPoint()
{
ActionId = ActionId,
CreatedDate = DateTime.Now,
ItemId = ItemId,
UserId = UserId,
PointsAccumulated = await GetPointsAwardedForAction(ActionId)
};
_entities.DailyActivityPoints.Add(dailyActivityPoint);
await _entities.SaveChangesAsync();
}
catch (Exception ex)
{
}
}
inside DoTask1 I am also calling an async method.
How can it be done and what's the best practice?
I believe you are encountering a thread-safety concern as described here. If so, you'll need to be sure that for each 'awaitable' that reaches into EF it is using its own DbContext instance.
So, be sure you aren't using a DbContext singleton; instantiate a new one as you go if you can or get tricky with a container like he does in the link (containers are your friends)
Maybe you should use this guideline in writing your asynchronous methods
Guideline:
Write the method the normal way you would write a conventional method, then convert it to an async method.
Use the async keyword in the method declaration.
public async Task<int> ExampleMethodAsync()
{
// . . . .
}
Use the await keyword in the code calling an asynchronous process/method.
int resultValue = await ExampleMethodAsync();
Note: await can only be used in an async method modified by the async keyword.
Use one of the correct return types in the async method in order to get results
return types for an async method need to be one of the following:
Task if your method has a return statement in which the operand has type TResult.
Task if your method has no return statement or has a return statement with no operand.
void if you're writing an async event handler.
Adding the “Async” suffix at the end of the calling method name. This is not required but is considered
a convention for writing async methods in C#.
public async Task<int> ExampleCallingMethodAsync()
{
// . . . .
}
Include at least one await expression in the async method code.
The suspension of an async method at an await expression doesn't
constitute an exit from the method, and finally blocks don’t run.
public async Task<int> ExampleMethodAsync()
{
//some code
string pageContents = await client.GetStringAsync(uri);
//some more code
return pageContents.Length;
}
Note:
You have to adjust what your async method is returning. The object being returned has to match the type Task<T> of your async method
If an async method doesn’t use an await operator to mark a suspension point, the method
executes as a synchronous method does, despite the async modifier. The compiler issues a
warning for such methods.
Below is a typical method converted to an asynchronous method
Example
private void WebPage(string someURI)
{
WebClient webClient = new WebClient();
string pageContent = webClient.DownloadString(someURI);
Console.WriteLine(pageContent);
}
changed to:
private async void WebPageAsync(string someURI)
{
WebClient webClient = new WebClient();
string pageContent = await webClient.DownloadStringTaskAsync(someURI);
Console.WriteLine(pageContent);
}
I hope this was helpful?
I've been reading examples for a long time now, but unfortunately I've been unable to apply the solutions to the code I'm working with. Some quick Facts/Assorted Info:
1) I'm new to C#
2) The code posted below is modified from Amazon Web Services (mostly stock)
3) Purpose of code is to compare server info to offline already downloaded info and create a list of need to download files. This snip is for the list made from the server side, only option with AWS is to call async, but I need this to finish before moving forward.
public void InitiateSearch()
{
UnityInitializer.AttachToGameObject(this.gameObject);
//these are the access key and secret access key for credentials
BasicAWSCredentials credentials = new BasicAWSCredentials("secret key", "very secret key");
AmazonS3Config S3Config = new AmazonS3Config()
{
ServiceURL = ("url"),
RegionEndpoint = RegionEndpoint.blahblah
};
//Setting the client to be used in the call below
AmazonS3Client Client = new AmazonS3Client(credentials, S3Config);
var request = new ListObjectsRequest()
{
BucketName = "thebucket"
};
Client.ListObjectsAsync(request, (responseObject) =>
{
if (responseObject.Exception == null)
{
responseObject.Response.S3Objects.ForEach((o) =>
{
int StartCut = o.Key.IndexOf(SearchType) - 11;
if (SearchType == o.Key.Substring(o.Key.IndexOf(SearchType), SearchType.Length))
{
if (ZipCode == o.Key.Substring(StartCut + 12 + SearchType.Length, 5))
{
AWSFileList.Add(o.Key + ", " + o.LastModified);
}
}
}
);
}
else
{
Debug.Log(responseObject.Exception);
}
});
}
I have no idea how to apply await to the Client.ListObjectsAsync line, I'm hoping you all can give me some guidance and let me keep my hair for a few more years.
You can either mark your method async and await it, or you can call .Wait() or .Result() on the Task you're given back.
I have no idea how to apply await to the Client.ListObjectsAsync line
You probably just put await in front of it:
await Client.ListObjectsAsync(request, (responseObject) => ...
As soon as you do this, Visual Studio will give you an error. Take a good look at the error message, because it tells you exactly what to do next (mark InitiateSearch with async and change its return type to Task):
public async Task InitiateSearchAsync()
(it's also a good idea to add an Async suffix to follow the common pattern).
Next, you'd add an await everywhere that InitiateSearchAsync is called, and so on.
I'm assuming Client.ListObjectsAsync returns a Task object, so a solution for your specific problem would be this:
public async void InitiateSearch()
{
//code
var collection = await Client.ListObjectsAsync(request, (responseObject) =>
{
//code
});
foreach (var item in collection)
{
//do stuff with item
}
}
the variable result will now be filled with the objects. You may want to set the return type of InitiateSearch() to Task, so you can await it too.
await InitiateSearch(); //like this
If this method is an event handler of some sort (like called by the click of a button), then you can keep using void as return type.
A simple introduction from an unpublished part of the documentation for async-await:
Three things are needed to use async-await:
The Task object: This object is returned by a method which is executed asynchronous. It allows you to control the execution of the method.
The await keyword: "Awaits" a Task. Put this keyword before the Task to asynchronously wait for it to finish
The async keyword: All methods which use the await keyword have to be marked as async
A small example which demonstrates the usage of this keywords
public async Task DoStuffAsync()
{
var result = await DownloadFromWebpageAsync(); //calls method and waits till execution finished
var task = WriteTextAsync(#"temp.txt", result); //starts saving the string to a file, continues execution right await
Debug.Write("this is executed parallel with WriteTextAsync!"); //executed parallel with WriteTextAsync!
await task; //wait for WriteTextAsync to finish execution
}
private async Task<string> DownloadFromWebpageAsync()
{
using (var client = new WebClient())
{
return await client.DownloadStringTaskAsync(new Uri("http://stackoverflow.com"));
}
}
private async Task WriteTextAsync(string filePath, string text)
{
byte[] encodedText = Encoding.Unicode.GetBytes(text);
using (FileStream sourceStream = new FileStream(filePath, FileMode.Append))
{
await sourceStream.WriteAsync(encodedText, 0, encodedText.Length);
}
}
Some thing to note:
You can specify a return value from an asynchronous operations with Task. The await keyword waits till the execution of the method finishes, and returns the string.
the Task object contains the status of the execution of the method, it can be used as any other variable.
if an exception is thrown (for example by the WebClient) it bubbles up at the first time the await keyword is used (in this example at the line string result (...))
It is recommended to name methods which return the Task object as MethodNameAsync
For more information about this take a look at http://blog.stephencleary.com/2012/02/async-and-await.html.
I'm learning how to use Tasks in MVC and I get completly lost. I need to download the source from selected webpage, find one element, get its value and return it. That's it.
For this I am using HtmlAgilityPack and HttpClient to fetch the webpage.
The problem that occurs is where nothing is waiting for response from httpClient and thus results that response generation completed when Task was still in progress. (An asynchronous module or handler completed while an asynchronous operation was still pending.)
I read lots of threads in here ,codeproj and some blogs, still don't understand what's the problem. Most common explanation is about resulting type of void in async method, but I cannot find any other way to return awaiting value, than this:
public float ReadPrice(Uri url)
{
switch (url.Host)
{
case "www.host1.xy":
return ParseXYZAsync(url).Result;
default:
return float.Parse("99999,99");
}
}
private Task<float> ParseXYZAsync(Uri url)
{
loadPage(url);
var priceNode = document.DocumentNode.SelectSingleNode(
#"//*[#id='pageWrapper']/div[4]/section[1]/div[4]/div[1]/div[1]/span");
var price = priceNode.InnerText;
...
return priceInFloat;
}
private async Task LoadPage(Uri url)
{
HttpClient http = new HttpClient();
var response = await http.GetByteArrayAsync(url);
String source = Encoding.GetEncoding("utf-8")
.GetString(response, 0, response.Length - 1);
source = WebUtility.HtmlDecode(source);
document.LoadHtml(source);
}
In order to figure out what's wrong you need to understand one key concept with async-await. When an async method hits the first await keyword, control is yielded back to the calling method. This means that when you do this:
loadPage(url);
The method will synchronously run until it hits:
var response = await http.GetByteArrayAsync(url);
Which will yields control back to ParseWebSite, which will continue execution and will probably end before the async operation has actually completed.
What you need to do is make LoadPage return a Task and await for it's completion:
private async Task<float> ParseWebsiteAsync(Uri url)
{
await LoadPageAsync(url);
var priceNode = document.DocumentNode.SelectSingleNode
(#"//*[#id='pageWrapper']/div[4]/section[1]/div[4]/div[1]/div[1]/span");
var price = priceNode.InnerText;
return priceInFloat;
}
private async Task LoadPageAsync(Uri url)
{
HttpClient http = new HttpClient();
var source = await http.GetAsStringAsync(url);
source = WebUtility.HtmlDecode(source);
document.LoadHtml(source);
}
Side Notes:
Do follow .NET naming conventions. Methods should be PascalCase and not camelCase
Do add the "Async" postfix for asynchronous methods. E.g, LoadPage should become LoadPageAsync.
private async void loadPage(Uri url) needs to return a Task:
private async Task loadPage(Uri url)
then you need to await it in the calling method:
private async Task<float> parseWEBSITEXY(Uri url)
{
await loadPage(url);
...
}
In you code load page starts and it returns immediately.
One more thing - it is not recommended to use async void, except for event handlers. Always return Task.
In this example from Microsoft, the method has a return type of Task<int>
Example 1:
async Task<int> AccessTheWebAsync()
{
// You need to add a reference to System.Net.Http to declare client.
HttpClient client = new HttpClient();
// GetStringAsync returns a Task<string>. That means that when you await the
// task you'll get a string (urlContents).
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
// You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork();
// The await operator suspends AccessTheWebAsync.
// - AccessTheWebAsync can't continue until getStringTask is complete.
// - Meanwhile, control returns to the caller of AccessTheWebAsync.
// - Control resumes here when getStringTask is complete.
// - The await operator then retrieves the string result from getStringTask.
string urlContents = await getStringTask;
// The return statement specifies an integer result.
// Any methods that are awaiting AccessTheWebAsync retrieve the length value.
return urlContents.Length;
}
In this second example, it uses async and await, BUT doesn't return a type of Task<>, why?
Example 2:
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
ReadCharacters();
}
static async void ReadCharacters()
{
String result;
using (StreamReader reader = File.OpenText("existingfile.txt"))
{
Console.WriteLine("Opened file.");
result = await reader.ReadToEndAsync();
Console.WriteLine("Contains: " + result);
}
}
}
}
Third, in the first example, is it possible to return an array(strings)?
In this second example, it uses async and await, BUT doesn't return a
type of Task<>, why?
They made an error. Whenever you create a method which is async and doesn't have a return value, it should return a Task. The only exception to that is event handlers, where you need to keep compatibility with the delegates signature, but this isn't the case. Think of a Task as the void equivalent of asynchronous methods.
Why do you actually want to return a Task and not void? Because returning a Task allows you to monitor the status of execution, and also lets you to properly handle any exceptions encapsulated inside the on going operation.
For example, think of an async void method that throws:
public async void WaitAndThrowAsync()
{
await Task.Delay(1000);
throw new Exception("yay");
}
public void CallWaitAndThrowAsync()
{
// What happens when it throws here?
WaitAndThrowAsync();
}
When you invoke this, you have no way to actually handle exceptions happening inside the method, it is "fire and forget" for the call-site. But when you expose a Task, you can now better handle that exception by asynchronously waiting:
public async Task WaitAndThrowAsync()
{
await Task.Delay(1000);
throw new Exception("yay");
}
public async Task CallWaitAndThrowAsync()
{
try
{
await WaitAndThrowAsync();
}
catch (Exception e)
{
// Do something.
}
}
Third, in the first example, is it possible to return an
array(strings)?
Yes, by returning a Task<string[]>:
public async Task<string> GetArrayAsync()
{
HttpClient client = new HttpClient();
var responseStream = await client.GetStreamAsync("http://msdn.microsoft.com");
using (var streamReader = new StreamReader(responseStream))
{
return await streamReader.ReadToEndAsync();
}
}
When you mark a method async, the compiler will implicitly create a Task for you. When you have a return type, the generated task is a Task<T> where T is your return type.
The second example is not a good example: with async void it's not possible anymore to await the asynchronous method. async void should be used for event handlers only.
For your second question, yes it's possible to return an array of strings.
Just use Task<string[]>.
I really recommend you to read this excellent article from Stephen Cleary: https://msdn.microsoft.com/en-us/magazine/jj991977.aspx
Some best practices from the article:
Avoid async void: Prefer async Task methods over async void methods. Exceptions: Event handlers
Async all the way: Don’t mix blocking and async code. Exceptions: Console main method
That second isn't according to the guidelines specified by Microsoft:
The return type is one of the following types:
- Task if your method has a return statement in which the operand has type TResult.
- Task if your method has no return statement or has a return statement with no operand.
- Void (a Sub in Visual Basic) if you're writing an async event handler.
Why should methods return Task or Task<T>?
Each returned task represents ongoing work. A task encapsulates information about the state of the asynchronous process and, eventually, either the final result from the process or the exception that the process raises if it doesn't succeed.
So you lose information when you use void. You discard the actual task progress and execution information. So for the void return type, use Task. For any other type, use Task<T>, where T is the actual return type.
Some methods can return void, but that is prohibited to event handler or starter methods:
An async method can also be a Sub method (Visual Basic) or have a void return type (C#). This return type is used primarily to define event handlers, where a void return type is required. Async event handlers often serve as the starting point for async programs.
I have a method which does a long action using an async task
Now, I want to add a cache mechanism that will be transparent in the same method.
Now, I could always fetch my cache result and wrap it with a Task so it will "work" but I want to prevent the context switch that I will get.
Here's an example of what I have:
var result = await LongTask();
private async Task<string> LongTask()
{
return await DoSomethingLong();
}
And here's an example of what I want:
var result = await LongTask();
private async Task<string> LongTask()
{
if(isInCache)
{
return cachedValue(); // cache value is a const string you can do return "1" instead.
}
// else do the long thing and return my Task<string>
return await DoSomethingLong();
}
Now I'm surprised to see that this compiled and worked
Something tells me that I'm not doing it correctly.
Here's another similar example that I've tested:
private async Task<string> DownloadString(bool sync)
{
using (WebClient wc = new WebClient())
{
var task = wc.DownloadStringTaskAsync("http://www.nba.com");
if(sync)
return task.Result;
return await task;
}
}
And here's the code:
var res = DownloadString(true);
string str1 = await res;
var res2 = DownloadString(false);
string str2 = await res2;
From what I've read here task.Result executes the task synchronously and returns a string.
Now I see the request via Fiddler and my program get's stuck on the return task.Result line even though I see a 200 OK and I wait a long time.
Bottom Line:
Whats the best\correct way to use caching inside an async method(e.g. doing something synchronously in some cases without create a context switch overhead?
Why does my second block of code with the DownloadString get stuck?
First of all, if after a call to an async method the returned task is already completed there would be no context switch, because none is needed. So this is completely acceptable:
private async Task<string> LongTask()
{
if(isInCache)
{
return cachedValue(); // cache value is a const string you can do return "1" instead.
}
// else do the long thing and return my Task<string>
return await DoSomethingLong();
}
However, in the cases where the result is cached, the async mechanism is redundant. This overhead is mostly negligible but you can improve performance by dropping both the async and await and create a completed task using Task.FromResult:
private Task<string> LongTask()
{
if(isInCache)
{
return Task.FromResult(cachedValue());
}
// else do the long thing and return my Task<string>
return DoSomethingLong();
}
...when you write “await someObject;” the compiler will generate code that checks whether the operation represented by someObject has already completed. If it has, execution continues synchronously over the await point. If it hasn’t, the generated code will hook up a continuation delegate to the awaited object such that when the represented operation completes, that continuation delegate will be invoked
From Async/Await FAQ
Task.Result doesn't execute the task synchronously, it waits synchronously. That means that the calling thread is blocked waiting for the task to complete. When you use that in an environment with a SynchronizationContext that may lead to a deadlock since the thread is blocked and can't handle the task's completion. You shouldn't use the Result property on a task that hasn't completed yet.