1

I have a wrapper over Microsoft.Azure.Cosmos.Container class. In some cases the only thing the wrapper does is calling the inner object's async method.

public Task<ItemResponse<T>> UpsertItemAsync<T>(T item, PartitionKey? partitionKey = null, ItemRequestOptions requestOptions = null, CancellationToken cancellationToken = default)
{
    return _container.UpsertItemAsync<T>(item, partitionKey, requestOptions, cancellationToken);
}

What is the best practice in this case? To add await before or to return the inner object task as is?

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
  • 1
    In this case it's fine to return the Task. But you have to be careful. For instance if you had this inside of a try/catch or a using block then you would want to do the `await` so you don't leave the blocks before the code is complete. – juharr May 04 '21 at 18:36
  • 1
    Stephen Cleary knows a thing or two about this. He says [do the await](https://blog.stephencleary.com/2016/12/eliding-async-await.html) – Caius Jard May 04 '21 at 18:40
  • 1
    @CaiusJard Actually at the very end he says _Do consider eliding when the method is just a passthrough or overload._ – juharr May 04 '21 at 18:47
  • @CaiusJard -- he says right on the page: "Do consider eliding when the method is just a passthrough or overload." So in this case, what he has is how he said it should be. – Andy May 04 '21 at 18:54
  • Pivotal word there being *consider* – Caius Jard May 04 '21 at 21:07
  • I don't believe you actually read the article you posted. – Andy May 04 '21 at 22:40

3 Answers3

1

David Fowler (ASP.NET Architect) has an excellent guidance.

I copy here the relevant part:

Prefer async/await over directly returning Task There are benefits to using the async/await keyword instead of directly returning the Task:

  • Asynchronous and synchronous exceptions are normalized to always be asynchronous.
  • The code is easier to modify (consider adding a using, for example).
  • Diagnostics of asynchronous methods are easier (debugging hangs etc).
  • Exceptions thrown will be automatically wrapped in the returned Task instead of surprising the caller with an actual exception.

❌ BAD This example directly returns the Task to the caller.

public Task<int> DoSomethingAsync()
{
    return CallDependencyAsync();
}

✅ GOOD This examples uses async/await instead of directly returning the Task.

public async Task<int> DoSomethingAsync()
{
    return await CallDependencyAsync();
}

NOTE: There are performance considerations when using an async state machine over directly returning the Task. It's always faster to directly return the Task since it does less work but you end up changing the behavior and potentially losing some of the benefits of the async state machine.

Peter Csala
  • 17,736
  • 16
  • 35
  • 75
0

It depends on what you want to achieve.

Here are the 2 case.

  1. If you add an await in this wrapper function - The control stops there waiting for upsert to finish. Once everything is done then control came back to from where this wrapper methord is called

  2. If you ignore an await - Now what happens is this control never wait to execute. It add's that async function to a thread pool using TPL which essentially runs it in background. But you never notice and the code never waits. It continues execution and immediately get back to where you called

2 things to note

If you await it, That line will return the Task result which can be int, bool, string or whatever

If you ignore await, That line will return a Task instead

There are many use cases for these 2 approaches.

  • If your caller requires output to continue, Use await here
  • If your caller just calls it and can continue (Not dependent on this upsert, Let it run in background) then don't use await
Sangeeth Nandakumar
  • 1,362
  • 1
  • 12
  • 23
-5

You have to add async to your wrapper declaration and await your inner object Task.. otherwise you will not be able to await your wrapper later in a caller context..

lachid
  • 1
  • 1
  • 1
    This is not true. You can await any method that returns `Task` or `Task`. `async` is only required if you need to do `await` within the method. – juharr May 04 '21 at 18:54
  • 1
    You can await any method which is returning Task, regardless of how exactly its built inside. – tym32167 May 04 '21 at 18:54