简体   繁体   中英

Simple means of making a function non-blocking / async in C#?

I have a button that - when the user clicks it - sends an email. I'd love for this to just return immediately and send the email in the background without holding up the UI as the email is processed. I've searched around for async / await /etc and I've found so many different approaches - I'm looking for a simple solution to this. My email code:

public void SendEmail(string toAddress, string subject, string body, string code = null) {
    try {

        var fromAddressObj = new MailAddress("noreply@me.com", "Name");
        var toAddressObj = new MailAddress(toAddress, toAddress);
        const string fromPassword = "Password";

        var smtp = new SmtpClient {
            Host = "smtp.office365.com",
            Port = 587,
            EnableSsl = true,
            DeliveryMethod = SmtpDeliveryMethod.Network,
            UseDefaultCredentials = false,
            Credentials = new NetworkCredential(fromAddressObj.Address, fromPassword)
        };
        using (var message = new MailMessage(fromAddressObj, toAddressObj) {
            Subject = subject,
            IsBodyHtml = true

        }) {
            message.Body = body;
            smtp.Send(message);
        }

    } catch (Exception e) {
        Elmah.ErrorSignal.FromCurrentContext().Raise(e);
    }
}

How can I modify this so that the caller is not blocked?

You could run your method in a task and not wait for it:

private void button_Click(object sender, EventArgs e)
{
   Task.Run( () => SendEMail(address, subject, body) );
}

SmtpClient has SendMailAsync so using it instead of Send will unblock the UI thread while sending but you can still handle any exception that may occur:

private async void button_Click(object sender, EventArgs e)
{
   await SendEmailAsync(address, subject, body)
}

public async Task SendEmailAsync(string toAddress, string subject, string body, string code = null) 
{
    try 
    {
        var fromAddressObj = new MailAddress("noreply@me.com", "Name");
        var toAddressObj = new MailAddress(toAddress, toAddress);
        const string fromPassword = "Password";

        var smtp = new SmtpClient {
            Host = "smtp.office365.com",
            Port = 587,
            EnableSsl = true,
            DeliveryMethod = SmtpDeliveryMethod.Network,
            UseDefaultCredentials = false,
            Credentials = new NetworkCredential(fromAddressObj.Address, fromPassword)
        };
        using (var message = new MailMessage(fromAddressObj, toAddressObj)
        {
            Subject = subject,
            IsBodyHtml = true
        }) 
        {
            message.Body = body;
            await smtp.SendMailAsync(message);
        }
    }
    catch (Exception e) 
    {
        Elmah.ErrorSignal.FromCurrentContext().Raise(e);
    }
}

If the synchronous part of the async method (the part before the await ) still manages to block the UI thread you can offload it to a ThreadPool thread:

private async void button_Click(object sender, EventArgs e)
{
   await Task.Run(() => SendEmailAsync(address, subject, body))
}

Notes:

  • Don't fire up a Task without await ing it unless you're in a situation where you can't use await (a UI event handler is not one of those cases)
  • async void is only appropriate for UI event handlers. Don't use it anywhere else.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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