BackgroundService.StartAsync, should it complete during startup or block?

Viewed 1520

I don't know if I'm missing some piece of this puzzle but as far as I'm used to working with services in general, any call to Start is supposed to always return so that you know your service has started.

I'm studying BackgroundService:
Background tasks with hosted services in ASP.NET Core
Implement background tasks in microservices with IHostedService and the BackgroundService class

In the examples on both, they're implementing ExecuteAsync (which is called by StartAsync) using a while loop meaning that ExecuteAsync (and by extension StartAsync) is only ever going to return when cancelled (like when the app shuts down).

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    while (!stoppingToken.IsCancellationRequested)
    {
        _logger.LogDebug($"GracePeriod task doing background work.");

        // This eShopOnContainers method is querying a database table
        // and publishing events into the Event Bus (RabbitMQ / ServiceBus)
        CheckConfirmedGracePeriodOrders();

        await Task.Delay(_settings.CheckUpdateTime, stoppingToken);
    }
}

This contradicts what I understand from services because how can you tell that the service has successfully started vs hanging on startup?

I'd really appreciate some clarity on this as I just can't seem to get my head around what's happening and I can't seem to find any further information specific to this.

Edit: Added call to ExecuteAsync from BackgroundService for illustration:

public virtual Task StartAsync(CancellationToken cancellationToken)
{
    // Store the task we're executing
    _executingTask = ExecuteAsync(_stoppingCts.Token);

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

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

Through the magic of async, even though ExecuteAsync appears to be an infinite loop, it really returns a Task to the caller. The await on the Delay is what allows control flow to leave the ExecuteAsync function, with the next iteration of the loop only running after that Task is resolved.

If you are not used to it, it can be easy to miss.

Related