Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call nested Async method

I want to know what is best way to call nested async methods, if I want both methods to be async.
First: Caller method must just return Task

public async Task SaveChangesAsync()
{
    await Context.SaveChangesAsync();
}
public Task UpdateAsync(List<TEntity> entities)
{
    foreach (TEntity entity in entities)
    {
        BeforeUpdate(entity);
        Context.Entry(entity).State = EntityState.Modified;
    }
    return SaveChangesAsync();
}


Second: Caller method must have an async-await as called method

public async Task SaveChangesAsync()
{
    await Context.SaveChangesAsync();
}
public async Task UpdateAsync(List<TEntity> entities)
{
    foreach (TEntity entity in entities)
    {
        BeforeUpdate(entity);
        Context.Entry(entity).State = EntityState.Modified;
    }
    await SaveChangesAsync();
}
like image 383
Vahid Jafari Avatar asked Jun 07 '16 06:06

Vahid Jafari


3 Answers

This code should not use async/await, unless there are pieces in those methods you have not shown.

In other words, this is how these methods should be written:

public Task SaveChangesAsync()
{
    return Context.SaveChangesAsync();
}
public Task UpdateAsync(List<TEntity> entities)
{
    foreach (TEntity entity in entities)
    {
        BeforeUpdate(entity);
        Context.Entry(entity).State = EntityState.Modified;
    }
    return SaveChangesAsync();
}

This is basically option 3 which you did not show, no trace of async/await at all.

The reason for this is that the way the code will execute is as follows:

  1. It will execute all the parts of UpdateAsync leading up to the await, on the calling thread, which means the foreach-loop will be executing on the calling thread
  2. It will execute the call to SaveChangesAsync() up until it returns, still on the calling thread
  3. SaveChangesAsync will call into Context.SaveChangesAsync() until it returns, still on the calling thread

In short, all the work of these methods have already been done on the calling thread, what is left is only the async-ness of Context.SaveChangesAsync. If you drop async/await completely and simply return the task that wrap that async-ness all the way back out, you have not sacrificed any functionality but you have removed all the complexity that the compiler generates for you with an async/await method.

The general rule is this:

Unless you need to do something after the async method returns, don't use async/await

Since both your methods ends right after the call to the async method, you're better off just returning the tasks involved instead and not using async/await.

like image 50
Lasse V. Karlsen Avatar answered Oct 12 '22 00:10

Lasse V. Karlsen


An await can work on any Task. It doesn't have to be the result of an async method. In fact, it doesn't have to be the result of any method at all.

That said, I recommend that any methods with non-trivial logic in them should use async/await:

public async Task UpdateAsync(List<TEntity> entities)
{
  foreach (TEntity entity in entities)
  {
    BeforeUpdate(entity);
    Context.Entry(entity).State = EntityState.Modified;
  }
  await SaveChangesAsync();
}

This is because async captures exceptions in the method body and places them on the returned Task, which is expected behavior when calling task-returning methods. If you elide the async/await here, then any exceptions from BeforeUpdate or setting State would be thrown directly rather than placed on the Task.

On the other hand, your calling method is trivial, so you can elide async/await there:

public Task SaveChangesAsync()
{
  return Context.SaveChangesAsync();
}
like image 27
Stephen Cleary Avatar answered Oct 12 '22 00:10

Stephen Cleary


When you go async, await and async need to be used through the call stack, otherwise the code will not be executed asynchronously. So use async on both methods.

If an async method doesn’t use an await operator to mark a suspension point, the method executes as a synchronous method does, despite the async modifier

https://msdn.microsoft.com/en-us/library/mt674882.aspx

like image 28
DvS Avatar answered Oct 11 '22 22:10

DvS