Are there performance concerns with `return await`?
No, there isn't any performance problem. It's just an unnecessary extra operation. It might take a bit longer to execute, but should be hardly noticeable. It's akin to return x+0
instead of return x
for an integer x
. Or rather, exactly equivalent to the pointless .then(x => x)
.
It doesn't do actual harm, but I'd consider it bad style and a sign that the author does not fully comprehend promises and async
/await
.
However, there's one case where it make an important difference:
try {
…
return await …;
} …
await
does throw on rejections, and in any case awaits the promise resolution before catch
or finally
handlers are executed. A plain return
would have ignored that.
async and await: are they bad?
Is there anything silly with this method? Note that when we converted
all method to non-async methods we got a heaps better performance.
I can see at least two things going wrong here:
public static async Task<T> GetApiResponse<T>(object parameters, string action, CancellationToken ctk)
{
using (var httpClient = new HttpClient())
{
httpClient.BaseAddress = new Uri(BaseApiAddress);
var formatter = new JsonMediaTypeFormatter();
return
await
httpClient.PostAsJsonAsync(action, parameters, ctk)
.ContinueWith(x => x.Result.Content
.ReadAsAsync<T>(new[] { formatter }).Result, ctk);
}
}
Firstly, the lambda you're passing to ContinueWith
is blocking:
x => x.Result.Content.ReadAsAsync<T>(new[] { formatter }).Result
This is equivalent to:
x => {
var task = x.Result.Content.ReadAsAsync<T>(new[] { formatter });
task.Wait();
return task.Result;
};
Thus, you're blocking a pool thread on which the lambda is happened to be executed. This effectively kills the advantage of the naturally asynchronous ReadAsAsync
API and reduces the scalability of your web app. Watch out for other places like this in your code.
Secondly, an ASP.NET request is handled by a server thread with a special synchronization context installed on it, AspNetSynchronizationContext
. When you use await
for continuation, the continuation callback will be posted to the same synchronization context, the compiler-generated code will take care of this. OTOH, when you use ContinueWith
, this doesn't happen automatically.
Thus, you need to explicitly provide the correct task scheduler, remove the blocking .Result
(this will return a task) and Unwrap
the nested task:
return
await
httpClient.PostAsJsonAsync(action, parameters, ctk).ContinueWith(
x => x.Result.Content.ReadAsAsync<T>(new[] { formatter }),
ctk,
TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext()).Unwrap();
That said, you really don't need such added complexity of ContinueWith
here:
var x = await httpClient.PostAsJsonAsync(action, parameters, ctk);
return await x.Content.ReadAsAsync<T>(new[] { formatter });
The following article by Stephen Toub is highly relevant:
"Async Performance: Understanding the Costs of Async and Await".
If I have to call an async method in a sync context, where using await
is not possible, what is the best way of doing it?
You almost never should need to mix await
and ContinueWith
, you should stick with await
. Basically, if you use async
, it's got to be async "all the way".
For the server-side ASP.NET MVC / Web API execution environment, it simply means the controller method should be async
and return a Task
or Task<>
, check this. ASP.NET keeps track of pending tasks for a given HTTP request. The request is not getting completed until all tasks have been completed.
If you really need to call an async
method from a synchronous method in ASP.NET, you can use AsyncManager
like this to register a pending task. For classic ASP.NET, you can use PageAsyncTask
.
At worst case, you'd call task.Wait()
and block, because otherwise your task might continue outside the boundaries of that particular HTTP request.
For client side UI apps, some different scenarios are possible for calling an async
method from synchronous method. For example, you can use ContinueWith(action, TaskScheduler.FromCurrentSynchronizationContext())
and fire an completion event from action
(like this).
Performance impact of using async await when its not necessary
In the first method compiler will generate "state machine" code around it and execution will be returned to the line return await service.GetAsync();
after task will be completed. Consider example below:
public async Task<Something> GetSomethingAsync()
{
var somethingService = new SomethingService();
// Here execution returns to the caller and returned back only when Task is completed.
Something value = await service.GetAsync();
DoSomething();
return value;
}
The line DoSomething();
will be executed only after service.GetAsync
task is completed.
Second approach simply starts execution of service.GetAsync
and return correspondent Task
to the caller without waiting for completion.
public Task<Something> GetSomethingAsync()
{
var somethingService = new SomethingService();
Task<Something> valueTask = service.GetAsync();
DoSomething();
return valueTask;
}
So in the example above DoSomething()
will be executed straight after line Task<Something> valueTask = service.GetAsync();
without waiting for completion of task.
Executing async
method on the another thread depend on the method itself.
If method execute IO
operation, then another thread will be only waste of the thread, which do nothing, only waiting for response. On my opinion async - await
are perfect approach for IO
operations.
If method GetAsync
contains for example Task.Run
then execution goes to the another thread fetched from thread pool.
Below is short example, not a good one, but it show the logic a tried to explain:
static async Task GetAsync()
{
for(int i = 0; i < 10; i++)
{
Console.WriteLine($"Iterate GetAsync: {i}");
await Task.Delay(500);
}
}
static Task GetSomethingAsync() => GetAsync();
static void Main(string[] args)
{
Task gettingSomethingTask = GetSomethingAsync();
Console.WriteLine("GetAsync Task returned");
Console.WriteLine("Start sleeping");
Thread.Sleep(3000);
Console.WriteLine("End sleeping");
Console.WriteLine("Before Task awaiting");
gettingSomethingTask.Wait();
Console.WriteLine("After Task awaited");
Console.ReadLine();
}
And output will be next:
Iterate GetAsync: 0
GetAsync Task returned
Start sleeping
Iterate GetAsync: 1
Iterate GetAsync: 2
Iterate GetAsync: 3
Iterate GetAsync: 4
Iterate GetAsync: 5
End sleeping
Before Task awaiting
Iterate GetAsync: 6
Iterate GetAsync: 7
Iterate GetAsync: 8
Iterate GetAsync: 9
After Task awaited
As you can see executing of GetAsync
starts straight after calling it.
If GetSomethingAsync()
will be changed to the:
static Task GetSomethingAsync() => new Task(async () => await GetAsync());
Where GetAsync
wrapped inside another Task, then GetAsync()
will not be executed at all and output will be:
GetAsync Task returned
Start sleeping
End sleeping
Before Task awaiting
After Task awaited
Of course you will need to remove line gettingSomethingTask.Wait();
, because then application just wait for task which not even started.
Async / await vs then which is the best for performance?
From a performance point of view, await
is just an internal version of .then()
(doing basically the same thing). The reason to choose one over the other doesn't really have to do with performance, but has to do with desired coding style or coding convenience. Certainly, the interpreter has a few more opportunities to optimize things internally with await
, but its unlikely that should be how you decide which to use. If all else was equal, I would choose await
for the reason cited above. But, I'd first choose which made the code simpler to write and understand and maintain and test.
Used properly, await
can often save you a bunch of lines of code making your code simpler to read, test and maintain. That's why it was invented.
There's no meaningful difference between the two versions of your code. Both achieve the same result when the axios call is successful or has an error.
Where await
could make more of a convenience difference is if you had multiple successive asynchronous calls that needed to be serialized. Then, rather than bracketing them each inside a .then()
handler to chain them properly, you could just use await
and have simpler looking code.
A common mistake with both await
and .then()
is to forget proper error handling. If your error handling desire in this function is to just return the rejected promise, then both of your versions do that identically. But, if you have multiple async calls in a row and you want to do anything more complex than just returning the first rejection, then the error handling techniques for await
and .then()
/.catch()
are quite different and which seems simpler will depend upon the situation.
Is it possible for an 'await' to resolve to a Promise?
No, it's impossible for an await
expression to result in a promise.
Just like you cannot fulfill a promise with another promise, and like then()
never calling the fulfillment handler with a promise.
does async/await nesting have performance consequences?
There is nothing in the microtask queue until promises resolve. Until then other tasks and (UI) events can be processed.
This is because the await
operator will make the corresponding async
function return immediately, allowing other JS code to execute. In your case the promise is returned by fetch
, which in practice will not resolve immediately. So there is nothing blocking here.
Then when the HTTP response makes the fetch
promise resolve, a microtask will indeed be created, which, when when executed will restore the corresponding async
function's execution context. Your example function has nothing else to do, so that is quickly done.
Note that it does not matter whether this function was originally called from within some other function: at this stage, only that particular function's execution context (in which an awaited promise resolved) is restored without any pre-existing callstack. So it does not return again to the wrapping function. That already happened in the first phase and will not happen again.
Then again there is free event processing until the next fetch
promise resolves. And so it continues.
Related Topics
Warn User Before Leaving Web Page with Unsaved Changes
How to Remove All Line Breaks from a String
Check/Uncheck Checkbox with JavaScript
Differencebetween 'New Object()' and Object Literal Notation
How to Determine User's Locale Within Browser
Comparing Date Part Only Without Comparing Time in JavaScript
Execute the Setinterval Function Without Delay the First Time
Extract Hostname Name from String
Interface Type Check with Typescript
Jquery .Ready in a Dynamically Inserted Iframe
JavaScript Before Leaving the Page
Asynchronously Load Images with Jquery
Call an Asynchronous JavaScript Function Synchronously
How to Escape Regular Expression Special Characters Using JavaScript
How to Listen to a "Style Change" Event
How to Prevent Enter Keypress to Submit a Web Form
Differencebetween String Primitives and String Objects in JavaScript