2014-11-25 82 views
6

我已經使用了一些異步編碼,但我並沒有真正理解如何使用它 - 雖然我理解了這個概念以及爲什麼需要它。想了解異步

這裏是我的設立:

我有一個Web API,我會從我的ASP.NET MVC應用程序,我的Web API將調用DocumentDB調用。在代碼示例中,我向DocumentDB發送查詢時看到很多await關鍵字。

我很困惑,如果我需要在我的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); 
} 

----網絡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; 
} 
+0

在你的情況下,當你說等待時你可以釋放一些資源。但是,可以使用tasks.whenall來改進 - 請閱讀以下內容:http://msdn.microsoft.com/en-AU/library/hh556530.aspx – codebased 2014-11-25 00:38:31

回答

6

你只能使用await如果該方法是asyncasync方法需要返回雖然void返回async方法是保留給事件處理TaskTask<T>void的方法中,因爲在他們內部拋出的異常被吞噬,你不能完成或鏈接後續任務。

我覺得你Index動作需要async並返回Task<ActionResult>和你CreateEmployee方法必須async以及它使用await裏面。

Best Practices in Asynchronous Programming有關何時以及如何使用async-await

1
async await 

他們是棘手的理解。

首先,在您的Web API方法中,您正在使用async而不等待。我相信你在那裏得到一些錯誤/警告嗎?

-

異步伺機用於返回工作線程返回給調用者,當你正在等待I/O操作完成。所以,是的,你確實想在你的MVC和Web API方面使用它。在繼續之前,請確保你明白這句話。

-

約異步事情/等待是,如果你使用它,你必須使用它通過調用函數一路,否則就沒有意義了(你會也得到錯誤/警告)。這意味着你使用的任何庫都必須支持它。在這種情況下,「DocumentClient」。按照慣例,支持它的方法將以「異步」結束,並且它將返回一個您可以等待的任務。

-

所以你的答案很簡單: 使用異步從一開始(控制器)等候,並儘量使其等待它要求任何長時間作業。如果這也是你的代碼,你應該能夠從那裏等待...並從那裏等待,直到你最終調用一些不是你的代碼的東西。如果你可以等待那些不屬於你的代碼,那麼你就被設置了。如果你不能,那麼你不應該從一開始就使用異步等待。

(沒辦法這是有道理的)

5

一些指導方針下面是我的解釋

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之後。 「之前」部分立即執行。當操作完成時,「後」部分正在執行。您可以通過任務API跟蹤第二部分操作,因爲您獲得對任務的引用。

一般而言,async允許將方法調用看作某種長操作,您可以通過任務API參考並等待直至完成並繼續執行另一段代碼。通過使用await通過ContinueWith呼叫通常它是相同的。

之前async/await/Task概念人在使用回調,但處理錯誤是如同地獄中的容易,Task相似,只不過它是能夠的callback概念允許更容易地處理異常。

一般來說這一切的任務/異步/等待,如果它發生,你已經使用jQuery/JavaScript的工作這裏有一個類似的概念是一個很好的問題,解釋它是如何做有「jQuery deferreds and promises - .then() vs .done()

口頭禪是接近 promises概念

編輯:我剛剛發現了.NET缺乏實施類似一個jQuery的/ JavaScript的發現then功能。

ContinueWithThen之間的區別在於Then是能夠撰寫的任務,並順序地執行它們而ContinueWith不是,它能夠僅在並行發射的任務,但它可以通過AWAIT構建體中容易地實現。這裏是我更新的代碼,包含整個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"); 
    } 
}