想要了解异步

时间:2014-11-25 00:32:50

标签: c# async-await asp.net-web-api2 azure-cosmosdb

我已经使用了异步编码,但我并不完全理解如何使用它 - 尽管我理解这个概念以及为什么需要它。

这是我的设置:

我有一个Web API,我将从我的ASP.NET MVC应用程序调用,我的Web API将调用DocumentDB。在代码示例中,我在向DocumentDB发送查询时看到很多等待关键字。

如果我需要在我的MVC应用程序异步中制作我的索引操作方法,我会感到困惑吗? 如果我的Web API中的CreateEmployee()方法应该是异步的,我也很困惑吗?

在这种情况下使用异步的正确方法是什么?

这是我的代码(此代码目前给我错误,因为我的MVC操作方法不是异步) ---- ASP.NET MVC应用程序代码----

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代码----

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;
}

3 个答案:

答案 0 :(得分:8)

这是我的解释

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")
    }
}

最终,Approach1Approach2都是相同的代码段。

async/await是Task API的语法糖。您需要async方法将其分为await之前和await之后的部分。 &#34;之前&#34;部分立即执行。 &#34;&#34;之后&#34;部分在await操作完成时执行。您可以通过Task API跟踪操作的第二部分,因为您获得了对任务的引用。

通常async允许将方法调用视为某种长操作,您可以通过Task API引用它并等到它完成并继续使用另一段代码。通过ContinueWith通过await使用async通常,它是相同的。

await / Task / Task概念之前人们使用回调,但处理错误就像地狱一样容易,callback类似于{promises的概念1}}除了它能够更容易地处理异常。

一般来说,所有这个Task / async / await mantra都接近于then的概念,如果你曾经使用过jQuery / JavaScript,那么这里有一个类似的概念,很不错问题解释它是如何完成的那样&#34; jQuery deferreds and promises - .then() vs .done()&#34;


编辑:我刚刚发现.NET缺少与jQuery / JavaScript相似的ContinueWith功能实现。

ThenThen之间的区别在于ContinueWith能够组成任务,并且在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"); } } 不能执行时顺序执行任务,它只能启动并行执行任务,但可以通过await结构轻松实现。这是我更新的代码,包含整个shebang:

{{1}}

答案 1 :(得分:7)

如果该方法为await并且async方法需要返回asyncTask或{{1},则您只能在方法中使用Task<T>虽然void返回void方法是为事件处理程序保留的,因为它们中的异常被吞下,你不能async完成它们或链后续任务。

我认为您的await操作需要Index并返回async,而您的Task<ActionResult>方法需要CreateEmployee以及它正在使用其中async

有关何时以及如何使用await

的一些指导,请参阅Best Practices in Asynchronous Programming

答案 2 :(得分:1)

async await

他们很难理解。

首先,在Web API的方法中,您使用async而不等待。我确定你有错误/警告吗?

-

async await用于在等待I / O完成时将工作线程返回给调用者。所以,是的,您确实希望在MVC和Web API方面都使用它。在继续之前请确保你理解这句话。

-

关于async / await的事情是,如果你使用它,你必须在调用函数中一直使用它,否则它没有意义(并且你会得到错误/也警告)。这意味着您使用的任何库都必须支持它。在这种情况下&#34; DocumentClient&#34;。按照惯例,支持它的方法将以#As; Async&#34;它将返回一个你可以等待的任务。

-

所以你的简短回答: 从一开始就使用async await(你的控制器),并试着让它等待它调用的任何长操作。如果这也是你的代码,你应该能够从那里等待......并等待那里......直到你最终调用的东西不是你的代码。如果您可以等待不属于您的代码,那么您就可以了。如果你不能,那么你不应该从一开始就使用async await。

(这没有任何意义)