2014-11-25 2 views
6

Я немного использовал асинхронное кодирование, но я действительно не совсем понимаю, как его использовать, хотя я понимаю концепцию и зачем мне это нужно.Хотите понять async

Вот мой набор вверх:

У меня есть веб-API, который я буду называть из моего приложения ASP.NET MVC, и мой Web API будет вызывать DocumentDB. В образцах кода я вижу много ожидающих ключевых слов при отправке запросов в DocumentDB.

Я смущен, если мне нужно сделать мой метод действия индекса в моем асинхронном приложении MVC? Я также смущен, если мой метод CreateEmployee() в моем веб-API должен быть асинхронным?

Каков правильный способ использования async в этом сценарии?

Вот мой код (этот код в настоящее время дает мне ошибки, потому что мой метод действия MVC не является асинхронным) ---- 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

В вашем случае вы освобождаете некоторые ресурсы, когда говорите, ожидаете. Однако вы можете улучшить с помощью tasks.whenall - прочитайте это: http://msdn.microsoft.com/en-AU/library/hh556530.aspx – codebased

ответ

6

вы можете использовать только await внутри метода, если этот метод является async и async методы должны возвращать Task, Task<T> или void хотя void возвращающегося async методы зарезервированы для обработчиков событий, потому что исключения, брошенные в них, проглатываются, и вы не можете выполнить их завершение или цепочку последующих задач.

Я думаю, что ваш Index действие должно быть async и возвращать Task<ActionResult> и ваш метод CreateEmployee должен быть async, а также он использует await внутри него.

См Best Practices in Asynchronous Programming для некоторых руководящих принципов, когда и как использовать async-await

1
async await 

Они сложно понять.

Прежде всего, в ваших методах в веб-API вы используете async без ожидания. Я уверен, что вы получаете некоторые ошибки/предупреждения там?

-

асинхронной AWAIT используются для возврата рабочей нити обратно к абоненту, когда вы ждете ввода/вывода должны быть завершены. Итак, да, вы хотите использовать его как в своей MVC, так и в веб-интерфейсе API. Пожалуйста, убедитесь, что вы поняли это предложение, прежде чем двигаться дальше.

-

Дело о асинхронном/ожидает, что, если вы используете его, вы должны использовать весь этот путь через функцию вызова, или иначе это не имеет смысла (и вы будете получить ошибки/предупреждение тоже). Это означает, что любая библиотека, которую вы используете, должна ее поддерживать. В этом случае «DocumentClient». По соглашению, поддерживающие его методы заканчиваются на «Async», и он вернет задание, которое вы можете ожидать.

-

Так что ваш короткий ответ: использование асинхронного ждет с самого начала (ваш контроллер), и попытаться сделать его ждет все, что долго операций она вызывает. Если это и ваш код, вы должны быть в состоянии ждать оттуда ... и ждать оттуда ... пока вы, наконец, не назовете что-то, что не является вашим кодом. Если вы можете дождаться этого кода, который не принадлежит вам, тогда вы настроены. Если вы не можете этого сделать, вы не должны использовать async, ожидающий самого начала.

(никоим образом это имеет смысл)

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

В конце концов, как Approach1 и Approach2 одинаковые куски кода.

async/await - синтаксический сахар вокруг Task API. Требуется, чтобы ваш метод async разбивал его на части до await, а после await. Часть «before» выполняется немедленно. Выполняется операция «после» после завершения операции await. Вы можете отслеживать вторую часть операции через API задач, так как вы получаете ссылку на задачу.

В общем, async позволяет обрабатывать вызов метода как своего рода длинную операцию, которую вы можете ссылаться через API-интерфейс задачи, и ждать, пока она не будет закончена, и продолжите с другой частью кода. Либо через ContinueWith звонок через await в целом это то же самое.

Перед async/await/Task концепции люди используют обратные вызовы, но обработка ошибок было так же легко, как ад, то Task похож на концепции callback кроме того, что он может позволить обработки исключений более легко.

В целом все это Задача/асинхронный/ждать мантра близка к концепции promises, если случится так, что вы работали с JQuery/JavaScript есть подобная концепция здесь хороший вопрос объяснить, как это делается там «jQuery deferreds and promises - .then() vs .done()»


Edit: Я только что узнал, что .NET не хватает реализации then функциональности, подобной найденной в JQuery/JavaScript.

Разница между ContinueWith и Then что 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"); 
    } 
} 
Смежные вопросы