Use async keyword only when required

While async keyword is very useful it shouldn’t be used when it isn’t required. Imagine this method

public async Task DoSomethingAsync()
{
	await Task.Delay(1000);
}

The method works but it has too much code generated unnecessarily. The thing is that async triggers a lot of code generation at compile time that is invisible at design time. When using .Net Reflector, compiled code looks like:

[AsyncStateMachine(typeof(<dosomethingasync>d__1)), DebuggerStepThrough]
public Task DoSomethingAsync()
{
	<dosomethingasync>d__1 stateMachine = new lt;dosomethingasync gt;d__1 {
		<>4__this = this,
		<>t__builder = AsyncTaskMethodBuilder.Create(),
		<>1__state = -1
	};
	stateMachine.<>t__builder.Start<<dosomethingasync>d__1>(ref stateMachine);
	return stateMachine.<>t__builder.Task;
}

[CompilerGenerated]
private sealed class lt;dosomethingasync>d__1 : IAsyncStateMachine
{
	public int <>1__state;
	public Program <>4__this;
	public AsyncTaskMethodBuilder <>t__builder;
	private TaskAwaiter <>u__1;

	private void MoveNext()
	{
		int num = this.<>1__state;
		try
		{
			TaskAwaiter awaiter;
			if (num != 0)
			{
				awaiter = Task.Delay(0x3e8).GetAwaiter();
				if (!awaiter.IsCompleted)
				{
					this.<>1__state = num = 0;
					this.<>u__1 = awaiter;
					Program.<dosomethingasync>d__1 stateMachine = this;
					this.<>t__builder.AwaitUnsafeOnCompleted gt;taskawaiter program.="" ,=""><dosomethingasync>d__1>(ref awaiter, ref stateMachine);
					return;
				}
			}
			else
			{
				awaiter = this.<>u__1;
				this.<>u__1 = new TaskAwaiter();
				this.<>1__state = num = -1;
			}
			awaiter.GetResult();
			awaiter = new TaskAwaiter();
		}
		catch (Exception exception)
		{
			this.<>1__state = -2;
			this.<>t__builder.SetException(exception);
			return;
		}
		this.<>1__state = -2;
		this.<>t__builder.SetResult();
	}

	[DebuggerHidden]
	private void SetStateMachine(IAsyncStateMachine stateMachine)
	{
	}
}

I won’t dig into what’s actually generated at this point, but while this code is perfectly functional and correct, it is all for nothing in this simple case. Since the only asynchronous operation is the last one, the original method can be easily rewritten as:

public Task DoSomethingAsync()
{
	return Task.Delay(1000);
}

This way it works the same except for the missing pile of generated code.

So, when there is a single asynchronous operation inside the method and no code is executed after it, the async (and await) keyboard can be omitted and you’ll save yourself from too much unnecessarily generated code plu the application will run a bit faster.

Comments (2) -

  • Sticky Kangaroo

    7.3.2016 23:04:50 | Reply

    Be careful with exceptions. If you return a task from inside a try block without await-ing then exceptions thrown by the async task won't be caught.

    Also be careful when returning tasks from inside using blocks. If the task isn't awaited inside the using block the using block will be exited and disposable resources will be disposed, possibly while the async task still depends on them.

    • Miha Markič

      16.3.2016 17:36:36 | Reply

      Absolutely. Hence it works only when "no code is executed after it"

Loading