Anduin Xue
Anduin Xue

Reputation: 3737

How to start an async method without await its completion?

Sometimes I need to start an async job which works very slow. I don't care if that job success and I need to continue working on my current thread.

Like sometimes I need to send an Email or SMS which works very slow. I need to respond to the web client as soon as possible so I don't want to await it.

I have googled this question and some articles suggest me to write like this:

// This method has to be async
public async Task<Response> SomeHTTPAction()
{
    // Some logic...
    // ...

    // Send an Email but don't care if it successfully sent.
    Task.Run(() =>  _emailService.SendEmailAsync());
    return MyRespond();
}

Or like this:

// This method has to be async
public async Task<Response> SomeHTTPAction()
{
    // Some logic...
    // ...

    // Send an Email but don't care if it successfully sent.
    Task.Factory.StartNew(() =>  _emailService.SendEmailAsync());
    return MyRespond();
}

There will be a warning says: before the call is completed. Consider applying the 'await' operator to the result of the call.

So what if I really awaited it? What is the best practice in C# to 'fire and forget', just call an async method without waiting for its completion?

Upvotes: 62

Views: 73336

Answers (7)

John Foll
John Foll

Reputation: 156

I did part of user2866442's solution, but I am using an Action with a Lambda to supply what is executed async:

Action<object, long> sendEmail = (var1, id) => 
{
    try
    {
        //Execute some code here
    }
    catch (Exception ex) 
    { 
        Log.ErrorException("sendEmail Error: " + ex.Message, ex); //Using NLog here.
    } 
    //Dont throw any errors here -it doesnt matter because this an async 
};

_ = System.Threading.Tasks.Task.Run(() => sendEmail(var1, id));  
//Run our sendMail code above without waiting for it to finish. 

Upvotes: -2

Cornelis
Cornelis

Reputation: 1107

If you truly just want to fire and forget. Simply don't call use await.

// It is a good idea to add CancellationTokens
var asyncProcedure = SomeHTTPAction(cancellationToken).ConfigureAwait(false);

// Or If not simply do:
var asyncProcedure = SomeHTTPAction().ConfigureAwait(false);

If you want to use the result output later its gets trickier. But if it is truly fire and forget the above should work

A Cancellation token allows interrupts and canceling procedures. If you are using Cancellation token you will need to use it everywhere from the retrieval straight through to the calling method (Turtles all the way down).

I used ConfigureAwait(false) to prevent deadlocks. Here for more information


EDIT

See the second answer that uses 'Task.Factory.StartNew' I gave this answer some time ago. At the time I didn't realise that the way I did it at the time doesn't ensure completion.

Upvotes: 34

user2866442
user2866442

Reputation: 829

A standalone discard is the best way to avoid this warning.

_ = Task.Run(() =>  _emailService.SendEmailAsync());

Discards are dummy variables and can be used to ignore the Task object returned by an asynchronous operation.

https://learn.microsoft.com/en-us/dotnet/csharp/discards#a-standalone-discard

Upvotes: 62

MrMaavin
MrMaavin

Reputation: 1741

If you need to use async in your function you can also use a discard variable and don't use await. This is also usefull if you have multiple async function calls but you don't need to wait for all of them.

public async function(){
    var tmp = await asyncfunction();
    ...
    _ = _httpClient.PutAsync(url, content);
    ...
}

Upvotes: 12

treendy
treendy

Reputation: 473

I am curious why this hasn't been suggested.

new Thread(() =>
{
    Thread.CurrentThread.IsBackground = true;
    //what ever code here...e.g.
    DoSomething();
    UpdateSomething();
}).Start();  

It just fires off a separate thread.

Upvotes: -1

Jawand Singh
Jawand Singh

Reputation: 2056

As Amadan told in the comment that, you need to remove async from your function. then it will stop giving you the warning.

// This method has to be async
public Response SomeHTTPAction()
{
     // Some logic...
     // ...
     // Send an Email but don't care if it successfully sent.
     Task.Factory.StartNew(() =>  _emailService.SendEmailAsync());
     return MyRespond();
}

and Task.Factory.StartNew(() => _emailService.SendEmailAsync()); will indeed work on a new thread.

Upvotes: 8

Dennis19901
Dennis19901

Reputation: 685

It all depends on what your Async method accepts. Normally it will accept a "special" class that also holds an event. You can subscribe your callback method to that event and pass it along with the method. When it's finished, your callback method will be called.

An example of this (for sockets) would be:

    public void CreateSocket()
    {
        Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        SocketAsyncEventArgs sockAsync = new SocketAsyncEventArgs();
        sockAsync.Completed += SockAsync_Completed;

        s.ConnectAsync(sockAsync);
    }

    private void SockAsync_Completed(object sender, SocketAsyncEventArgs e)
    {
        //Do stuff with your callback object.
    }

It all depends on what the method you are trying to call can accept. I would look at the documentation for more help on that specifically.

Upvotes: 0

Related Questions