Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Want to understand async

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;
}
like image 757
Sam Avatar asked Nov 25 '14 00:11

Sam


3 Answers

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");
    }
}
like image 141
Lu4 Avatar answered Sep 21 '22 14:09

Lu4


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

like image 43
NeddySpaghetti Avatar answered Sep 22 '22 14:09

NeddySpaghetti


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)

like image 30
hatcyl Avatar answered Sep 21 '22 14:09

hatcyl