Jonas
Jonas

Reputation: 3283

Returning from async methods

I have a async method returning a task. Do I need to return anything in the following situation?

    public async Task UpdateProductSpecificationsAsync()
    {
        await _productRepository.UpdateProductSpecificationsAsync(); //Takes long time to execute
    }

Is the following code examples equivalent to each other?

    public Task UpdateProductSpecifications()
    {
        _productRepository.UpdateProductSpecifications(); //Takes long time to execute
        return Task.FromResult(0);
    }

    public Task UpdateProductSpecifications()
    {
        _productRepository.UpdateProductSpecifications(); //Takes long time to execute
        return Task.CompletedTask;
    }

Is there any situation where I should await a Task.TaskCompleted?

Upvotes: 0

Views: 1235

Answers (1)

Peter Duniho
Peter Duniho

Reputation: 70701

I have a async method returning a task. Do I need to return anything in the following situation?

An async Task method is equivalent to a non-async void method. And like a non-async void method, an async Task method can't return anything.

That said, your example seems odd. If all you're going to do in the method is await some other async method, you should not bother with the async/await at all. Just return the Task directly:

public Task UpdateProductSpecificationsAsync()
{
    return _productRepository.UpdateProductSpecificationsAsync();
}

Is the following code examples equivalent to each other?

Define "equivalent". To me, they are definitely not equivalent. The first creates a new Task<int> object, and returns that object as a Task. Calling code would have to cast it back to Task<int> to see the result value.

The second returns the static Task.CompletedTask property value, which is a singleton object, allocated only once per process. There is no result value for the caller to read.

The caller would have to go to extra work to directly observe those differences, but at the very least, returning a reference to a singleton object is more efficient than creating a new object every time. Whether this is significant or not depends, of course, on how often you call the method.

All that said, I don't understand why in that scenario, you wouldn't implement it like this (assuming there's no truly asynchronous version of the _productRepository method):

public Task UpdateProductSpecificationsAsync()
{
    return Task.Run(() => _productRepository.UpdateProductSpecifications());
}

Then you'd get actual asynchronous behavior, which is what the caller would typically expect. Your versions force the caller to wait in spite of the method looking like it's asynchronous. IMHO, it's a very bad idea to write code that lies.

Is there any situation where I should await a Task.TaskCompleted [sic]?

You mean directly? Or by code that doesn't know it's been handed the Task.CompletedTask reference? I'm going to assume the former, because it's unreasonable to expect the latter to have any idea that's what it's doing.

That seems like an overly broad question to me. Any situation? That's open to interpretation.

That said, I would say "no" to that question. What would the point of awaiting something you know is already completed? The await won't yield back to the caller, because the awaitable is already completed. It seems like a waste to me.

I can imagine scenarios where one awaits a Task.CompletedTask in order to achieve some other desired side-effect. I've seen weirder things than that in production code. But I wouldn't recommend it. Whatever hypothetical side-effect we'd be talking about, I'm sure it's not a documented side-effect, so one would be relying on undocumented implementation details that could change in the future.

Don't write fragile code. Write code that relies only on documented behavior, and only does things that make obvious sense. (And the corollary: if you break that rule, at the very least write a very detailed comment explaining why you wrote weird, fragile code.)

Upvotes: 4

Related Questions