Why do I have to use await for a method to run asy

2019-02-01 12:30发布

问题:

This question already has an answer here:

  • Fire-and-forget with async vs “old async delegate” 4 answers

I've been pouring through MSDN docs all day, and their philosophy of asynchronous coding is confusing me. As I understand it, the thread that calls the async method will not be blocked if the async method is called. Yet, async is always paired in examples with await, which appears to negate the async-ness, making it so that the outer method DOES have to wait for the code to execute anyway. Shouldn't I be able to call an async method and then continue with the execution of the outer method?

This is the scenario I've been encountering, more or less:

void reportSomethingHappened(info)
    - Collect info
    - HTTP POST info to logging server (ie. mixpanel, sentry)

And here would be a calling method:

void largerProcess
    if (whatever)
        reportSomethingHappened();
    bla;
    bla;

As far as I understand, since POST requests can be done asynchronously, I should be able to make reportSomethingHappened() into an async method (by, AFAIK, await-ing the webrequest, and adding the async keyword).

But the largerProcess method doesn't need to wait for (ie. await) the reporting method to finish in order to execute bla bla. Yet, VS tells me that with an async method I can either await it, or it will happen synchronously, and block. Doesn't that defeat the purpose of doing it separately?

How do I write this so that reportSomethingHappened doesn't block execution of largerProcess? (Which is inherently confusing me, because I thought that was the point of async all along)

回答1:

If you call an asynchronous method it will run asynchronously whether you await the returned task or not.

await doesn't affect how the method executes, only how you as the caller deals with it. You can call the async method, get the task and await it right away (which is the simplest option). That will enable you to write code that looks synchronous but runs asynchronously as await basically registers the rest of the code after it as a callback to be executed only after the awaited task is completed. This doesn't block in the traditional since as no thread is blocked, but the code flow will be sequential:

async Task LargerProcessAsync()
{
    if (condition)
    {
        await ReportSomethingHappenedAsync();
    }

    // do other stuff
}

However, you don't absolutely need to do that. You can get the task back, do other stuff and only then await it:

async Task LargerProcessAsync()
{
    Task task = null;
    if (condition)
    {
        task = ReportSomethingHappenedAsync();
    }

    // do other stuff

    if (task != null)
    {
        await task;
    }
}

Or you can simply remove the await completely. You should realize though that this can be dangerous as the task can be faulted and the exception can go unobserved and that's why it's discouraged. There are several ways to do this right, but they aren't simple. You can use Task.ContinueWith:

void LargerProcess()
{
    if (condition)
    {
        ReportSomethingHappenedAsync().ContinueWith(task => 
        {
            try
            {
                task.Wait();
            }
            catch (Exception exception)
            {
                // handle exception
            }
        })
    }

    // do other stuff
}

Or for ASP.Net look at Fire and Forget on ASP.NET