2

I'm just learning how to use async and await. Say I have the following method that sends an email:

public async void Send()
{
    // Create a network credentials object
    var credentials = new NetworkCredential(azureUserName, azurePassword);

    // Create an Web transport for sending the email
    var transportWeb = new Web(credentials);

    // Send the email
    await transportWeb.DeliverAsync(this._email);
}

This code will exist in an MVC app I'm creating. The user will initiate some action in the browser and a call will get made to a controller action method and this email method will eventually get called within the action method.

My understanding is that as soon as the last line is executed (the line with the await), control immediately returns to the caller, while the DeliverAsync method completes its task. Assuming that is correct, let's also assume the email is taking a long time to send, maybe 30 seconds. Will the controller action method return control back to the browser even though the DeliverAsync method is still attempting to execute the send? Ideally this is what I would to have happen.

Randy Minder
  • 47,200
  • 49
  • 204
  • 358
  • Side note: you can get into all kind of nasty state if `DeliverAsync` throws exceptions. Check out proper Fire-and-Forget code in http://stackoverflow.com/a/27852439/477420... Also as @usrpointed out in they proper answer - consider finding better way instead of `async void`... – Alexei Levenkov May 11 '15 at 20:46

2 Answers2

5

Your understanding is correct. Don't use async void though, there is no need. Use async Task and be sure to process errors. Right now you will never find out about bugs causing crashes.

Will the controller action method return control back to the browser even though the DeliverAsync method is still attempting to execute the send?

That depends on what that method does. It's code is not here. Right now it has no way of awaiting Send so probably yes.

It is also important to note that background work in ASP.NET can die at any time when the worker process exits (deployment, reboot, crash, ...).

usr
  • 168,620
  • 35
  • 240
  • 369
  • If we assume the last line in the action controller method is something like email.SendAsync(); then you're saying controller action method will return control to the browser, regardless how long the email takes to send? Also, thanks for the tip about returning Task. – Randy Minder May 11 '15 at 20:43
  • @RandyMinder - yes. You can do anything even if you wanted to - since there is no way for `async void` to let caller to wait for completion – Alexei Levenkov May 11 '15 at 20:47
  • Should probably flag that "return control back to the browser" might be a bit confusing. Async will not begin to render the page until all code has run. The only performance benefit is that the thread will return to the ASP.NET thread pool where it can serve another request. Async does not enable early serving of http requests (from my, limited, understanding). – Underscore May 11 '15 at 20:52
  • @Underscore if you don't await a task then it runs in the background. Just like a thread. The request can be completed before that task is finished. – usr May 11 '15 at 20:59
  • @usr Ah, sorry, I overlooked your clarification that you were talking about the controller action not awaiting ```Send```. – Underscore May 11 '15 at 21:03
3

Will the controller action method return control back to the browser even though the DeliverAsync method is still attempting to execute the send?

From the fact your method is async void, I'm assuming the controller is simply invoking the method call and returning, so yes, it will end up returning even though the message wasn't actually sent yet.

I would not recommend you use such an approach as for the fact that any exception would be rethrown on an arbitrary threadpool thread and makes no guarantee of actually completing properly. I'm assuming delivery of the message is important to you.

Instead, I'd recommend creating a queue which is fed the request when your controller is invoked, and a different thread that dequeues the queue, properly sends the message while asynchronously waiting for its completion, and perhaps logging any error which have occured. In the fashion of a producer-consumer style of execution.

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • 1
    Much better of `a different thread that dequeues the queue` is in a separate process, such as a windows service, that is not subject to being killed through several mechanisms (as threads in ASP.Net are). – Eric J. May 11 '15 at 21:47
  • @Eric "Much better" is very subjective. No doubt that a windows service ir a separate process is more *robust*, but is it really needed here? Not sure. Only the OP can say. – Yuval Itzchakov May 12 '15 at 04:36
  • I have a hard time imagining a scenario where it's OK for an email to be non-delivered because IIS was restarted, but perhaps that is OK for the OP. – Eric J. May 12 '15 at 07:15
  • @EricJ. You can always register work with ASP.NET runtime to make sure the work has time to complete before IIS terminating. – Yuval Itzchakov May 12 '15 at 07:21