kalle1
kalle1

Reputation: 43

Async & await always returns "Waiting for completion"

I have a application where I use Async and Await when calling a rest web service. When running my unit tests, I can't seem to get any proper response back even though I am using await. This is the async method:

public async Task<Response> SendEmail(string apiKey, string senderEmail, string senderName, string recipientEmail, string recipientName, string subject, string content, bool html)
{
    var client = new SendGridClient(apiKey);
    var from = new EmailAddress(senderEmail, senderName);
    var to = new EmailAddress(recipientEmail, recipientName);
    var msg = MailHelper.CreateSingleEmail(from, to, subject, html ? null : content, html ? content : null);
    var response = await client.SendEmailAsync(msg);

    return response;
}

The calling method looks like this:

public static object SendEmail(string apiKey, string senderEmail, string senderName, string recipientEmail, string recipientName, string subject, string content, bool html)
{
    EmailHandler emailHandler = new EmailHandler();
    var response =  emailHandler.SendEmail(apiKey, senderEmail, senderName, recipientEmail, recipientName, subject, content, html);

    return response;
}

Now if I put a breakpoint on return response in the calling function, I can see an object that has the status="Waiting for Activation" and Result="Not yet computed". Of what I have been able to gather, calling .Result on the returned object should make it run synchronously and return the result. (For example, status code of the request such as 200).

What am I missing here? Why does it not wait until it is finished?

Upvotes: 1

Views: 4367

Answers (3)

ColinM
ColinM

Reputation: 2681

async-await are just keywords which tell the compiler to compile your code as an implementation of an IAsyncStateMachine and also wait for it to finish, both keywords need to be used in conjunction for this to work and they only work on Task objects. A Task represents a piece of work that is happening.

You must mark your SendEmail method as async, change the return type to Task<object> and await your emailHandler.SendEmail call.

Bottom line is, if you're going async then you must go async all the way up and await at some or many points, otherwise you begin looking at running asynchronous code synchronously which is kind of shooting yourself in the foot.

public static async Task<object> SendEmail(string apiKey, string senderEmail, string senderName, string recipientEmail, string recipientName, string subject, string content, bool html)
{
    EmailHandler emailHandler = new EmailHandler();
    var response = await emailHandler.SendEmail(apiKey, senderEmail, senderName, recipientEmail, recipientName, subject, content, html);

    return response;
}

As always, Stephen Cleary is an excellent source for async knowledge.

Upvotes: 5

Hans Kilian
Hans Kilian

Reputation: 25070

It seems to me that you expect SendEmail to be synchronous. You can wait for the asynchronous method to complete in SendEmail like this:

public static Response SendEmail(string apiKey, string senderEmail, string senderName, string recipientEmail, string recipientName, string subject, string content, bool html)
{
    EmailHandler emailHandler = new EmailHandler();
    var responseTask =  emailHandler.SendEmail(apiKey, senderEmail, senderName, recipientEmail, recipientName, subject, content, html);
    responseTask.Wait(); // Wait for the task to complete

    return responseTask.Result;
}

Upvotes: -4

BerDev
BerDev

Reputation: 1805

It's because

var response =  emailHandler.SendEmail(apiKey, senderEmail, senderName, recipientEmail, recipientName, subject, content, html);

initiaties a Task, and your caller (public static object SendEmail) doesn't await that task.

Chris Pratt has written a nice blog with an async-helper to run async-methods sync.

Upvotes: 2

Related Questions