Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I start not-awaited background tasks in an async method?

I'm struggling with how to perform some very-long-running background processing in a world of async methods.

Using the vocabulary from Stephen Cleary's blog, I'm interested in kicking off a "delegate task" after await-ing a "promise task". I want to return the promised value as soon as it's available, and let the delegate task continue on in the background.

To handle exceptions in the un-await-ed delegate task, I'll use an exception-handling continuation modeled after the one described here.

public async Task<int> ComplexProcessAsync()
{
  ...
  int firstResult = await FirstStepAsync();

  // THE COMPILER DOESN'T LIKE THIS
  Task.Run(() => VeryLongSecondStepIDontNeedToWaitFor(firstResult)).LogExceptions();

  return firstResult;
}

Since VeryLongSecondStep...() could take many minutes, and its results are observable purely by side-effect (e.g. records appearing in a database), I reeeaaalllyy don't want to await it.

But as pointed out, the compiler doesn't like this. It warns "Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call."

I could ignore the warning, but I get the feeling I'm not doing this in "correct" way. So what is the correct way?

Thanks!

like image 343
David Rubin Avatar asked Nov 20 '15 19:11

David Rubin


Video Answer


1 Answers

I could ignore the warning, but I get the feeling I'm not doing this in "correct" way. So what is the correct way?

The compiler is warning you that you're not awaiting the task. From my experience, >99% of the time the compiler is correct, the code is wrong because it's missing an await. So this is one of the rare situations where you know what you're doing and want to live dangerously. :)

In this case, you can explicitly assign the task to an unused variable:

var _ = Task.Run(() => VeryLongSecondStepIDontNeedToWaitFor(firstResult));

The compiler is intelligent enough to understand that you're using this variable to silence the "missing await" warning.

On a side note, I do not recommend ContinueWith at all; in the async world, it's just a more dangerous version of await. So I'd write LogExceptions something like this:

public static async Task LogExceptions(this Task task)
{
  try
  {
    await task.ConfigureAwait(false);
  }
  catch (Exception ex)
  {
    LogException(ex);
  }
}
like image 60
Stephen Cleary Avatar answered Sep 19 '22 13:09

Stephen Cleary