2014-11-25 3 views
6

Ich habe Async-Codierung ein wenig verwendet, aber ich verstehe nicht wirklich, wie man es benutzt - obwohl ich das Konzept verstehe und warum ich es brauche.Willst du async verstehen

Hier ist mein Set up:

ich einen Web-API, die ich von meinem ASP.NET MVC-app und meinem Web-API DocumentDB rufen nennen. In Codebeispielen sehe ich viele abwartende Schlüsselwörter beim Senden von Abfragen an DocumentDB.

Ich bin verwirrt, wenn ich meine Indexaktionsmethode in meiner MVC-App async machen muss? Ich bin auch verwirrt, wenn meine CreateEmployee() -Methode in meiner Web-API async sein sollte?

Was ist der richtige Weg, um Async in diesem Szenario zu verwenden?

Hier ist mein Code (Dieser Code zur Zeit mir Fehler geben, weil meine MVC Aktionsmethode nicht async ist) ---- 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; 
} 
+0

In Ihrem Fall befreien Sie einige Ressourcen, wenn Sie sagen, warten Sie. Sie können jedoch verbessern, indem Sie tasks.whenall verwenden - bitte lesen Sie dies: http://msdn.microsoft.com/en-AU/library/hh556530.aspx – codebased

Antwort

6

Sie nur await innerhalb einer Methode verwenden können, wenn diese Methode ist async und async Methoden Task, Task<T> oder void obwohl void Rückkehr async Methoden vorbehalten zurückkehren müssen, sind für Event-Handler, weil die in ihnen geworfenen Ausnahmen werden verschluckt und Sie können nicht ihre await ihre Abschluss- oder Kettenfolgeaufgaben erledigen.

Ich denke, Ihre Index Aktion async sein muss und zurück eine Task<ActionResult> und Ihre CreateEmployee Methode async auch sein muss, wie es await im Innern verwendet.

Siehe Best Practices in Asynchronous Programming für einige Richtlinien, wann und wie async-await

1
async await 

Sie sind schwierig zu verstehen.

Zuerst, in Ihren Methoden in Web-API verwenden Sie Async ohne warten. Ich bin sicher, dass Sie einige Fehler/Warnungen bekommen, richtig?

-

async await werden verwendet, um den Arbeitsfaden zurück an den Aufrufer zurück, wenn Sie für I/O warten abgeschlossen werden. Also, ja, Sie möchten es sowohl in Ihrer MVC- als auch in Ihrer Web-API-Seite verwenden. Bitte stellen Sie sicher, dass Sie diesen Satz verstehen, bevor Sie fortfahren.

-

Die Sache über async/erwarten ist, dass, wenn Sie es verwenden, müssen Sie es den ganzen Weg durch die Anruffunktionen nutzen, sonst macht es keinen Sinn machen (und Sie werden bekomme Fehler/warnt auch). Dies bedeutet, dass die von Ihnen verwendete Bibliothek diese unterstützen muss. In diesem Fall "DocumentClient". Per Konvention werden die Methoden, die sie unterstützen, in "Async" enden und eine Aufgabe zurückgeben, auf die Sie warten können.

-

So Ihre kurze Antwort: Verwendung asynchroner erwarten von Anfang an (der Controller), und versuchen Sie es, was es nennt lange Operationen zu machen zu warten. Wenn das auch dein Code ist, solltest du in der Lage sein, von dort zu warten ... und von dort auf dich warten ... bis du schließlich etwas anrufst, das nicht dein Code ist. Wenn Sie den Code erwarten können, der nicht Ihnen gehört, dann sind Sie eingestellt. Wenn Sie nicht können, dann sollten Sie async von Anfang an nicht verwenden.

(keine Möglichkeit, dies machte Sinn)

5

verwenden Hier ist meine Erklärung

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

Schließlich beide Approach1 und Approach2 sind identische Teile des Codes. Die async/await ist syntaktische Zucker um Task API. Es dauert Ihre async Methode teilt es in Teile vor await und nach await. Der "Vorher" -Teil wird sofort ausgeführt. Der "Nachher" -Teil wird ausgeführt, wenn der await Vorgang abgeschlossen ist. Sie können den zweiten Teil des Vorgangs über die Task-API verfolgen, da Sie einen Verweis auf eine Aufgabe erhalten.

Im Allgemeinen async ermöglicht es, einen Methodenaufruf als eine Art langer Operation zu behandeln, die Sie über die Task-API referenzieren können, und warten Sie, bis sie beendet ist, und fahren Sie mit einem anderen Code fort. Entweder über ContinueWith Anruf von via await im Allgemeinen ist es das gleiche.

Vor async/await/Task Konzepte Menschen Rückrufe verwendet haben, aber Handhabungsfehler wie die Hölle so einfach war, ist die Task ähnlich einem Konzept von callback außer dass es Ausnahmen leichter erlauben können Handhabung.

Im Allgemeinen all diese Aufgabe/Asynchron/erwartet Mantra Konzept der promises nahe ist, wenn es passiert, dass Sie mit jQuery/JavaScript gearbeitet habe es ein ähnliches Konzept hier ist eine schöne Frage zu erklären, wie es dort „jQuery deferreds and promises - .then() vs .done()

getan

bearbeiten: ich habe gerade herausgefunden, dass .NET-Implementierung von then Funktionalität ähnlich einer fehlt in jQuery/JavaScript gefunden.

Der Unterschied zwischen ContinueWith und Then ist, dass Then der Lage ist Aufgabe zu komponieren, und sie der Reihe nach auszuführen, während ContinueWith nicht der Fall, ist es in der Lage nur Aufgabe parallel zu starten, aber es kann einfach über das await Konstrukt umgesetzt werden. Hier ist mein aktualisierter Code, der den ganzen Shebang enthält:

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