[英]async Task with HttpClient
public async Task<IReadOnlyCollection<OrderItem>> GetOrderItemsAsync()
{
var items = await httpClient.GetFromJsonAsync<IReadOnlyCollection<CatalogItemDto>>("items");
return items;
}
我有一個具有異步HttpClient
的異步Task
方法。 HttpClient
和 async Task
如何協同工作?
我確實知道什么是異步任務,但是結合上面的例子,我想知道它在幕后是如何工作的。
請幫助我了解幕后的細節。 目前,每當我看到異步Task
時,我只是在不知道內部編譯器如何執行的情況下等待。
一個正確但也有點無用的答案是: async/await
在后台創建 state 機器。
但我們可以做得更好。 讓我們從什么是任務開始。 這是一個 object,其中包含一些 function 應該在將來的某個時間點調用(作為對某些事件的反應)。 如果 function 返回某些內容(或引發異常),則結果將存儲在 Task 中以供以后使用。
為了執行這些任務,您需要一個 TaskScheduler。 這是一些 object,它決定何時、如何以及執行哪些任務、在哪些線程上、對哪些事件做出反應等。例如,如果我們有一個從磁盤讀取並返回任務的方法,那么它開始從磁盤讀取,它立即返回一個Task,並告訴TaskScheduler“嘿,一旦我從磁盤讀取完畢,為我執行這個Task,好嗎?”。 因此,從磁盤讀取的操作發生在后台(畢竟是 i/o),同時 CPU 可以自由地做其他事情。
Tasks 的另一個特點是它們可以被鏈接。 意思是,我們可以將一個新任務附加到舊任務上,這將在舊任務完成后執行。 在過去,我們會這樣做:
// Say these two are given.
Task<User> GetUserByEmail(string email);
Task<List<Order>> GetOrdersForUser(int userId);
Task<List<Order>> GetOrdersByEmail(string email)
{
var tcs = new TaskCompletionSource<List<Order>>();
GetUserByEmail(email) // T1
.ContinueWith((prevTask) =>
{
// This will be executed only when T1 is done.
GetOrdersForUser(prevTask.Result.Id) // T2
.ContinueWith((prevTask) =>
{
// This will be executed when T2 is done.
tcs.TrySetResult(prevTask.Result);
});
});
return tcs.Task;
}
但是人們意識到,當發生大量異步調用時,這種編碼很快就會變得不可讀。 這也稱為“回調地獄”(取自 JavaScript 的術語)。
async/await
所做的只是解決了這個可讀性問題。 事實上,通過應用它,上面的代碼可以重寫為:
async Task<List<Order>> GetOrdersByEmail(string email)
{
var user = await GetUserByEmail(email);
return await GetOrdersForUser(user.Id);
}
好多了,你不覺得嗎? 從技術上講,這些是不一樣的(我沒有在“回調地獄”版本中進行任何錯誤處理,這會使情況變得更糟)。 編譯器實際上為async/await
生成了不同的代碼(所謂的異步 state 機器,例如,您可以通過查看https://sharplab.io/來了解它的工作原理),但在功能上它們非常接近。
async/await
的全部意義在於讓代碼更簡潔。 這是一個句法特征。
我有一個具有同步
HttpClient
的異步Task
方法。
HttpClient
既不是同步的也不是異步的。 只有方法可以是同步的或異步的。 而你使用GetFromJsonAsync
的具體方法是異步的,它甚至名稱中都有Async
。 :) 好吧,它不一定是異步的(這實際上取決於實現,實際上您可以編寫一個返回已完成任務的同步代碼),但它確實返回了一個我們可以等待的 Task。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.