简体   繁体   English

带有 HttpClient 的异步任务

[英]async Task with HttpClient

public async Task<IReadOnlyCollection<OrderItem>> GetOrderItemsAsync()
{
    var items = await httpClient.GetFromJsonAsync<IReadOnlyCollection<CatalogItemDto>>("items");
    return items;
}

I have an asynchronous Task method that has an asynchronous HttpClient .我有一个具有异步HttpClient的异步Task方法。 How do HttpClient and async Task work together? HttpClient和 async Task如何协同工作?

I do know what asynchronous task is, but combining both for the above example, I wanted to know how it works behind the scene.我确实知道什么是异步任务,但是结合上面的例子,我想知道它在幕后是如何工作的。

Please help me understand the nitty gritty behind the scene.请帮助我了解幕后的细节。 Currently whenever I see async Task I just put await without knowing internally how the compiler performs.目前,每当我看到异步Task时,我只是在不知道内部编译器如何执行的情况下等待。

A correct, but also a bit useless, answer is: async/await creates a state machine in the background.一个正确但也有点无用的答案是: async/await在后台创建 state 机器。

But we can do better.但我们可以做得更好。 Lets start with what a Task is.让我们从什么是任务开始。 This is an object that holds some function inside that is supposed to be called at some point in the future (as a reaction to some event).这是一个 object,其中包含一些 function 应该在将来的某个时间点调用(作为对某些事件的反应)。 If that function returns something (or throws an exception) the result will be stored on the Task to be used later.如果 function 返回某些内容(或引发异常),则结果将存储在 Task 中以供以后使用。

In order to execute those Tasks you need a TaskScheduler.为了执行这些任务,您需要一个 TaskScheduler。 This is some object that decides when, how and what Tasks are executed, on what threads, reacting to what events, etc. So for example if we have a method that reads from a disk and returns Task, then it starts reading from the disk, it immediatly returns a Task, and tells the TaskScheduler "hey, once I finish reading from the disk, execute this Task for me, ok?".这是一些 object,它决定何时、如何以及执行哪些任务、在哪些线程上、对哪些事件做出反应等。例如,如果我们有一个从磁盘读取并返回任务的方法,那么它开始从磁盘读取,它立即返回一个Task,并告诉TaskScheduler“嘿,一旦我从磁盘读取完毕,为我执行这个Task,好吗?”。 Thus the operation of reading from the disk happens in the background (it is i/o after all) and the CPU is free to do other things in the meantime.因此,从磁盘读取的操作发生在后台(毕竟是 i/o),同时 CPU 可以自由地做其他事情。

And another feature of Tasks is that they can be chained. Tasks 的另一个特点是它们可以被链接。 Meaning, we can attach a new Task to old Task, which will be executed after the old Task is done.意思是,我们可以将一个新任务附加到旧任务上,这将在旧任务完成后执行。 In the old days we would do something like this:在过去,我们会这样做:

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

But people realized that this kind of coding can quickly become unreadable when lots of async calls happens.但是人们意识到,当发生大量异步调用时,这种编码很快就会变得不可读。 This is also known as "callback hell" (the term taken from JavaScript).这也称为“回调地狱”(取自 JavaScript 的术语)。

What async/await does is simply it solves this readability issue. async/await所做的只是解决了这个可读性问题。 Indeed, by applying it, the code above can be rewritten as:事实上,通过应用它,上面的代码可以重写为:

async Task<List<Order>> GetOrdersByEmail(string email)
{
    var user = await GetUserByEmail(email);
    return await GetOrdersForUser(user.Id);
}

Much better, don't you think?好多了,你不觉得吗? Technically these are not the same (I didn't do any error handling in "callback hell" version, which would make it even worse).从技术上讲,这些是不一样的(我没有在“回调地狱”版本中进行任何错误处理,这会使情况变得更糟)。 The compiler actually produces a different code for async/await (so called async state machine, you can see how it works by looking at https://sharplab.io/ for example), but functionally these are quite close.编译器实际上为async/await生成了不同的代码(所谓的异步 state 机器,例如,您可以通过查看https://sharplab.io/来了解它的工作原理),但在功能上它们非常接近。

The whole point of async/await is simply to make the code cleaner. async/await的全部意义在于让代码更简洁。 It is a syntactic feature.这是一个句法特征。

I have an asynchronous Task method that has a synchronous HttpClient .我有一个具有同步HttpClient的异步Task方法。

HttpClient is neither synchronous or asynchronous. HttpClient既不是同步的也不是异步的。 Only methods can be synchronous or asynchronous.只有方法可以是同步的或异步的。 And the concrete method you use GetFromJsonAsync is asynchronous, it even has Async in the name.而你使用GetFromJsonAsync的具体方法是异步的,它甚至名称中都有Async :) Well, it is not necessarily asynchronous (this actually depends on the implementation, in fact you could write a synchronous code that returns a completed task), but it does return a Task that we can await. :) 好吧,它不一定是异步的(这实际上取决于实现,实际上您可以编写一个返回已完成任务的同步代码),但它确实返回了一个我们可以等待的 Task。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM