Is it ok to return CompletedTask from BackgroundService.ExecuteAsync?

71 views Asked by At

I have the following two alternatives:

protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
     _ = ForeverRunningLoop(stoppingToken);

     return Task.CompletedTask;
}

private Task ForeverRunningLoop(CancellationToken token)
{
     while(true)
     {
          token.ThrowIfCancellationRequested();
          // Do some asynchronous work...
     }
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
     await ForeverRunningLoop(stoppingToken);
}

private Task ForeverRunningLoop(CancellationToken token)
{
     while(true)
     {
          token.ThrowIfCancellationRequested();
          // Do some asynchronous work...
     }
}

Is there any difference between those? If so, which one is better?

2

There are 2 answers

1
Stephen Cleary On BEST ANSWER

In this specific scenario, the Task returned from ExecuteAsync is ignored, so there's no behavioral difference between the two.

I would still recommend the second option (or just merge the methods completely as suggested in the comments), because it looks wrong to do a fire-and-forget discard like that. Code like the first option is a major red flag in literally any scenario other than ExecuteAsync.

0
Florent Bunjaku On

They will behave the same, only in rare cases when Task returned from ForeverRunningLoop fails before returning the results, the second one will bubble up the exception.

BackgroundService source code

public virtual Task StartAsync(CancellationToken cancellationToken)
        {
            // Create linked token to allow cancelling executing task from provided token
            _stoppingCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);

            // Store the task we're executing
            _executeTask = ExecuteAsync(_stoppingCts.Token);

            // If the task is completed then return it, this will bubble cancellation and failure to the caller
            if (_executeTask.IsCompleted)
            {
                return _executeTask;
            }

            // Otherwise it's running
            return Task.CompletedTask;
        }