Edit: for clarification, this is in a Blazor Server application
I'm confused about the correct usage of InvokeAsync and updating the UI. The documentation has several usages without really explaining the reason for awaiting or not awaiting. I have also seen some contradictory threads without much to backup their reasoning.
It seems wrong to make all methods async to await InvokeAsync(StateHasChanged) and I read somewhere that the reason InvokeAsync was introduced was to prevent the need for async code everywhere. But then what situations might I want to await it?
awaiting:
https://learn.microsoft.com/en-us/aspnet/core/blazor/components/?view=aspnetcore-5.0&viewFallbackFrom=aspnetcore-3.0#invoke-component-methods-externally-to-update-state
non-await discard:
https://learn.microsoft.com/en-us/aspnet/core/blazor/components/rendering?view=aspnetcore-5.0
Here are some examples of different usages that I have seen, if anyone could explain or share any links with information on some of the differences between them that'd be great (thanks!)
public void IncrementCounter()
{
_counter++;
InvokeAsync(StateHasChanged);
}
public void IncrementCounter()
{
InvokeAsync(() =>
{
_counter++;
StateHasChanged);
}
}
public async Task IncrementCounter()
{
_counter++;
await InvokeAsync(StateHasChanged);
}
public async Task IncrementCounter()
{
await InvokeAsync(() =>
{
_counter++;
StateHasChanged();
});
}
IncrementCounter (a ButtonClick handler) is the wrong thing to look at - it always runs on the SyncContext thread and can always use a plain StateHasChanged() without Invoke.
So lets look at a Timer event instead. The Threading.Timer class does not support async handlers so you run in a void Tick() { ... } on an unspecified thread.
You do need InvokeAsync(StateHasChanged) here. You could make the Tick method an async void just to await the InvokeAsync but that gives of the wrong signals. Using InvokeAsync without await is the lesser evil.
void Tick() // possibly threaded event handler
{
_counter++;
InvokeAsync(StateHasChanged); // fire-and-forget mode
}
But when you are in an async method and still need InvokeAsync, it is neater to await it, just because you can.
async Task SomeService()
{
_counter++;
await InvokeAsync(StateHasChanged);
}
Related
I’m struggling a bit to understand why the following code doesn’t work (note: this is a contrived example, I am trying to understand how to use doWhile).
bool busy = true;
void doSomething() async {
await Future.doWhile(() => busy);
print("done");
}
void doSomethingElse() async {
await Future.delayed(
Duration(seconds: 2), (){
busy = false;
});
}
void main() {
print("a");
doSomething();
doSomethingElse();
print("b");
}
I would have thought that this would print:
a
b
done
But it instead just blocks permanently (or at least on dartpad it does). Could someone please help me understand what is wrong here?
My best guess is that the doWhile is repeatedly scheduling itself as soon as it is called so that no other code can ever be executed. If that is the case, how can one use doWhile normally?
(I am aware of Completer and similar, I’m just specifically interested in doWhile here)
I am struggling to understand how Task.ContinueWith is supposed to work in Asp.Net WebApi. I wonder if someone can explain how, given the example below.
Consider the following example:
public IHttpActionResult Get()
{
DoStuff()
.ContinueWith(task =>
{
_log.Debug("Log some stuff");
});
return Ok();
}
public async Task DoStuff()
{
await Task.Delay(1000);
}
From what I have read I was expecting the continuation function to execute (even when the web request has completed), but it never does. If I reduce the Task.Delay down to <10ms it works. I assume that when the web request returns, the continuation function is "lost", but why is that, is it not just a piece of work waiting to be completed on a ThreadPool thread???
Please help! Thanks..
We basically have a class that looks like this below that is using the Castle.DynamicProxy for Interception.
using System;
using System.Collections.Concurrent;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Castle.DynamicProxy;
namespace SaaS.Core.IoC
{
public abstract class AsyncInterceptor : IInterceptor
{
private readonly ILog _logger;
private readonly ConcurrentDictionary<Type, Func<Task, IInvocation, Task>> wrapperCreators =
new ConcurrentDictionary<Type, Func<Task, IInvocation, Task>>();
protected AsyncInterceptor(ILog logger)
{
_logger = logger;
}
void IInterceptor.Intercept(IInvocation invocation)
{
if (!typeof(Task).IsAssignableFrom(invocation.Method.ReturnType))
{
InterceptSync(invocation);
return;
}
try
{
CheckCurrentSyncronizationContext();
var method = invocation.Method;
if ((method != null) && typeof(Task).IsAssignableFrom(method.ReturnType))
{
var taskWrapper = GetWrapperCreator(method.ReturnType);
Task.Factory.StartNew(
async () => { await InterceptAsync(invocation, taskWrapper).ConfigureAwait(true); }
, // this will use current synchronization context
CancellationToken.None,
TaskCreationOptions.AttachedToParent,
TaskScheduler.FromCurrentSynchronizationContext()).Wait();
}
}
catch (Exception ex)
{
//this is not really burring the exception
//excepiton is going back in the invocation.ReturnValue which
//is a Task that failed. with the same excpetion
//as ex.
}
}
....
Initially this code was:
Task.Run(async () => { await InterceptAsync(invocation, taskWrapper)).Wait()
But we were losing HttpContext after any call to this, so we had to switch it to:
Task.Factory.StartNew
So we could pass in the TaskScheduler.FromCurrentSynchronizationContext()
All of this is bad because we are really just swapping one thread for another thread. I would really love to change the signature of
void IInterceptor.Intercept(IInvocation invocation)
to
async Task IInterceptor.Intercept(IInvocation invocation)
And get rid of the Task.Run or Task.Factory and just make it:
await InterceptAsync(invocation, taskWrapper);
The problem is Castle.DynamicProxy IInterecptor won't allow this. I really want do an await in the Intercept. I could do .Result but then what is the point of the async call I am calling? Without being able to do the await I lose out of the benefit of it being able to yield this threads execution. I am not stuck with Castle Windsor for their DynamicProxy so I am looking for another way to do this. We have looked into Unity, but I don't want to replace our entire AutoFac implementation.
Any help would be appreciated.
All of this is bad because we are really just swapping one thread for another thread.
True. Also because the StartNew version isn't actually waiting for the method to complete; it will only wait until the first await. But if you add an Unwrap() to make it wait for the complete method, then I strongly suspect you'll end up with a deadlock.
The problem is Castle.DynamicProxy IInterecptor won't allow this.
IInterceptor does have a design limitation that it must proceed synchronously. So this limits your interception capabilities: you can inject synchronous code before or after the asynchronous method, and asynchronous code after the asynchronous method. There's no way to inject asynchronous code before the asynchronous method. It's just a limitation of DynamicProxy, one that would be extremely painful to correct (as in, break all existing user code).
To do the kinds of injection that is supported, you have to change your thinking a bit. One of the valid mental models of async is that a Task returned from a method represents the execution of that method. So, to append code to that method, you would call the method directly and then replace the task return value with an augmented one.
So, something like this (for return types of Task):
protected abstract void PreIntercept(); // must be sync
protected abstract Task PostInterceptAsync(); // may be sync or async
// This method will complete when PostInterceptAsync completes.
private async Task InterceptAsync(Task originalTask)
{
// Asynchronously wait for the original task to complete
await originalTask;
// Asynchronous post-execution
await PostInterceptAsync();
}
public void Intercept(IInvocation invocation)
{
// Run the pre-interception code.
PreIntercept();
// *Start* the intercepted asynchronous method.
invocation.Proceed();
// Replace the return value so that it only completes when the post-interception code is complete.
invocation.ReturnValue = InterceptAsync((Task)invocation.ReturnValue);
}
Note that the PreIntercept, the intercepted method, and PostInterceptAsync are all run in the original (ASP.NET) context.
P.S. A quick Google search for async DynamicProxy resulted in this. I don't have any idea how stable it is, though.
I have the following WebAPI action and is wondering if returning Task<bool> and return _store.ContainerExistsAsync(container) directly is better;
I ask because, if I understand async/await correctly, the compiler creates a statemachine at the await to return to the same state. Returning the task directly without having to await it in the action, would that be theoretical faster?
public async Task<HttpResponseMessage> GetContainer(string container)
{
if (container.Length < 3 ||
container.Length > 63 ||
!Regex.IsMatch(container, #"^[a-z0-9]+(-[a-z0-9]+)*$"))
return Request.CreateResponse(HttpStatusCode.BadRequest, new { errors = new string[1] { "Container Name is not alowed." } })
return Request.CreateResponse<bool>(HttpStatusCode.OK, await _store.ContainerExistsAsync(container));
}
Yes, if you can implement an asynchronous method without async and await, then go ahead; async/await will add overhead.
This is commonly seen when the last line of a method has the only await and looks like return await ...;
In your particular example, I'm not 100% sure whether this would work since the method is doing something after the await.
It's easy enough to make it return the Task<bool> from ContainerExistsAsync directly, but the error handling would also need to change. If throwing a HttpResponseException works well enough, then yes, you would be able to implement an asynchronous method without using async.
Consider the following (based on the default MVC template), which is a simplified version of some "stuff" that happens in the background - it completes fine, and shows the expected result, 20:
public ActionResult Index()
{
var task = SlowDouble(10);
string result;
if (task.Wait(2000))
{
result = task.Result.ToString();
}
else
{
result = "timeout";
}
ViewBag.Message = result;
return View();
}
internal static Task<long> SlowDouble(long val)
{
TaskCompletionSource<long> result = new TaskCompletionSource<long>();
ThreadPool.QueueUserWorkItem(delegate
{
Thread.Sleep(50);
result.SetResult(val * 2);
});
return result.Task;
}
However, now if we add some async into the mix:
public static async Task<long> IndirectSlowDouble(long val)
{
long result = await SlowDouble(val);
return result;
}
and change the first line in the route to:
var task = IndirectSlowDouble(10);
then it does not work; it times out instead. If we add breakpoints, the return result; in the async method only happens after the route has already completed - basically, it looks like the system is unwilling to use any thread to resume the async operation until after the request has finished. Worse: if we had used .Wait() (or accessed .Result), then it will totally deadlock.
So: what is with that? The obvious workaround is "don't involve async", but that is not easy when consuming libraries etc. Ultimately, there is no functional difference between SlowDouble and IndirectSlowDouble (although there is obvious a structural difference).
Note: the exact same thing in a console / winform / etc will work fine.
It's to do with the way the synchronization context is implemented in ASP.NET (Pre .NET 4.5). There's tons of questions about this behavior:
Task.WaitAll hanging with multiple awaitable tasks in ASP.NET
Asp.net SynchronizationContext locks HttpApplication for async continuations?
In ASP.NET 4.5, there's a new implementation of the sync context that's described in this article.
http://blogs.msdn.com/b/webdev/archive/2012/11/19/all-about-httpruntime-targetframework.aspx
When you use .Result there is always a possibility of deadlock because .Result is blocking by nature. The way to avoid deadlocks is to not block on Tasks (you should use async and await all the way down). The subject is in details described here:
Don't Block on Async Code
One fix is to add ConfigureAwait:
public static async Task<long> IndirectSlowDouble(long val)
{
long result = await SlowDouble(val).ConfigureAwait(false);
return result;
}
Another fix is to use async/await throughout:
public async Task<ActionResult> Index()
{
var task = IndirectSlowDouble(10);
long result = await task;
ViewBag.Message = result.ToString();
return View();
}