await and event handler - c#

It's permitted to convert and usual event handler from void to Task based and await it like below?
Something.PropertyChanged += async (o, args) => await IsButtonVisible_PropertyChanged(o, args);
Something.PropertyChanged -= async (o, args) => await IsButtonVisible_PropertyChanged(o, args);
private Task IsButtonVisible_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
if (IsSomthingEnabled)
{
return SomeService.ExecuteAsync(...);
}
return Task.CompletedTask;
}
Or do it like this?
Something.PropertyChanged += IsButtonVisible_PropertyChanged;
Something.PropertyChanged -= IsButtonVisible_PropertyChanged;
private void IsButtonVisible_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
if (IsSomthingEnabled)
{
_ = SomeService.ExecuteAsync(...);
}
}
Update:
Or this one, I know that the use Task void It should be banned, because exception it's not catched, but maybe for the case of an Eventhandler it's ok since the Eventhandler doesn't return.
Something.PropertyChanged += IsButtonVisible_PropertyChanged;
Something.PropertyChanged -= IsButtonVisible_PropertyChanged;
private async void IsButtonVisible_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
if (IsSomthingEnabled)
{
await = SomeService.ExecuteAsync(...);
}
}

The syntax for asynchronous event handlers is :
Something.PropertyChanged += IsButtonVisible_PropertyChanged;
...
private async void IsButtonVisible_PropertyChanged(object sender,
PropertyChangedEventArgs e)
{
if (IsSomethingEnabled)
{
await SomeService.ExecuteAsync(...);
}
}
This allows awaiting asynchronous operations inside the event handler without blocking the UI thread. This can't be used to await for an event in some other method though.
Awaiting a single event
If you want some other code to await for an event to complete you need a TaskCompletionSource. This is explained in Tasks and the Event-based Asynchronous Pattern (EAP).
public Task<string> OnPropChangeAsync(Something x)
{
var options=TaskCreationOptions.RunContinuationsAsynchronously;
var tcs = new TaskCompletionSource<string>(options);
x.OnPropertyChanged += onChanged;
return tcs.Task;
void onChanged(object sender,PropertyChangedEventArgs e)
{
tcs.TrySetResult(e.PropertyName);
x.OnPropertyChanged -= onChanged;
}
}
....
async Task MyAsyncMethod()
{
var sth=new Something();
....
var propName=await OnPropertyChangeAsync(sth);
if (propName=="Enabled" && IsSomethingEnabled)
{
await SomeService.ExecuteAsync(...);
}
}
This differs from the example in two places:
The event handler delegate gets unregistered after the event fires. Otherwise the delegate would remain in memory as long as Something did.
TaskCreationOptions.RunContinuationsAsynchronously ensures that any continuations will run on a separate thread. The default is to run them on the same thread that sets the result
This method will await only a single event. Calling it in a loop will create a new TCS each time, which is wasteful.
Awaiting a stream of events
It wasn't possible to easily await multiple events until IAsyncEnumerable was introduced in C# 8. With IAsyncEnumerable<T> and Channel, it's possible to create a method that will send a stream of notifications :
public IAsyncEnumerable<string> OnPropChangeAsync(Something x,CancellationToken token)
{
var channel=Channel.CreateUnbounded<string>();
//Finish on cancellation
token.Register(()=>channel.Writer.TryComplete());
x.OnPropertyChanged += onChanged;
return channel.Reader.ReadAllAsync();
async void onChanged(object sender,PropertyChangedEventArgs e)
{
channel.Writer.SendAsync(e.PropertyName);
}
}
....
async Task MyAsyncMethod(CancellationToken token)
{
var sth=new Something();
....
await foreach(var prop in OnPropertyChangeAsync(sth),token)
{
if (propName=="Enabled" && IsSomethingEnabled)
{
await SomeService.ExecuteAsync(...);
}
}
}
In this case, only one event handler is needed. Every time an event occurs the property named is pushed to the Channel. Channel.Reader.ReadAllAsync() is used to return an IAsyncEnumerable<string> that can be used to loop asynchronously. The loop will keep running until the CancellationToken is signaled, in which case the writer will go into the Completed state and the IAsyncEnumerable<T> will terminate.

Quoting from Microsoft's article Async/Await - Best Practices in Asynchronous Programming, and specifically from the Avoid async void section:
Void-returning async methods have a specific purpose: to make asynchronous event handlers possible. [...] Event handlers naturally return void, so async methods return void so that you can have an asynchronous event handler.
Based on this, your third approach is the correct one:
private async void IsButtonVisible_PropertyChanged(object sender,
PropertyChangedEventArgs e)
{
if (IsSomethingEnabled)
{
await SomeService.ExecuteAsync();
}
}
Your first approach (+= async (o, args) => await) is technically equivalent, but it's not recommended because it is idiomatic and may cause confusion to future maintainers.
Your second approach (_ = SomeService.ExecuteAsync() launches the asynchronous operation in a fire-and-forget fashion, which is rarely a good idea because your application completely loses track of this task. It also elides async and await, which opens another can of worms.

the syntax for an async Event Handler is
async void handler(object sender,EventArgs args){}
and as Events don't return there is nothing to await for, so waiting for them is pointless
however if you need a response from an event then you can use the EventsArgs class to provide the response, eg
class FeedbackEventArgs:EventArgs
{
event EventHandler Completed;
Complete(){
this.Completed(this,EventArgs.Empty);
}
}
then you can use it as
event EventHandler<FeedbackEventArgs> myFeedbackEvent;
args = new FeedbackEventArgs();
args.Completed += OnCompleted;
this.myFeedbackEvent(this,args)
note if your handler is not async then you can assume that you code was paused while the event occurred, in which case you can just read a property from the eventArg rather than having to trigger an event
class FeedbackEventArgs:EventArgs
{
int result{get;set;}
}
event EventHandler<FeedbackEventArgs> myFeedbackEvent;
this.myFeedbackEvent(this,args)
args.result //this will be the result set in the sync handler
as noted by #Panagiotis this is a conceptual example not a working example

Related

EditContext OnFieldChanged reporting wrong return type

I have the 3 methods below in a razor file
protected override async Task OnInitializedAsync()
{
EditContext = new EditContext(_projectModel);
EditContext.OnFieldChanged += EditContext_OnFieldChanged;
}
private async Task EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
{
await SetOkDisabledStatus();
}
This method is an async method and I have to await it anywhere it is been called
private async Task SetOkDisabledStatus()
{
if (EditContext.Validate())
{
OkayDisabled = null;
await JsRuntime.InvokeVoidAsync("Animate");
}
else
{
OkayDisabled = "disabled";
}
}
I am using the EditContext for validation in a Blazor server application.
I have been getting the error message on this line below in the OnInitializedAsync() method and not sure how to proceed with it.
EditContext.OnFieldChanged += EditContext_OnFieldChanged;
Error Message:
Task MyProject.EditContext_OnFieldChanged(object, FieldChangedEventArgs)'
has the wrong return type.
Expected a method with void EditContext_OnFieldChanged(object?, FieldChangedEventArgs e)
Please note that I am using sonarqube to check all my code.
You can assign an async lambda to the event handler, like this:
EditContext.OnFieldChanged +=
async (sender,args) => await EditContext_OnFieldChanged(sender,args);
But, you should be aware that the EditContext/Form will not await your task. Anything you do in that async task will be out of sync with the editcontext.
You should probably include a cancellation token in your async code as well, so that multiple changes to a field do not fire multiple validation tasks at the same time.
Async validation is hard - make sure you test every possible scenario.
Generated Blazor eventhandlers (like #onclick="...") are flexible about return type and parameters but EditContext.OnFieldChanged is not, it has a fixed delegate type.
Make the following change:
//private async Task EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
private async void EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
{
await SetOkDisabledStatus();
StateHasChanged(); // make sure OkayDisabled takes effect
}
On another note, you can probably make OkayDisabled a boolean and use disabled="#OkayDisabled" where you need it.
Blazor makes the disabled attribute disappear when you assign it with false.
Alternative: keep the validation synchronous. That might prevent some problems as #Mister Magoo points out. And then let only the Animation run async.
private void EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
{
SetOkDisabledStatus();
}
private void SetOkDisabledStatus()
{
if (EditContext.Validate())
{
OkayDisabled = null;
_ = JsRuntime.InvokeVoidAsync("Animate"); // no await, on purpose
}
else
{
OkayDisabled = "disabled";
}
}
StateHasChanged() should not be needed in this scenario.

EventHandler with Task as return value

The base C# EventHandler is defined as:
namespace System
{
public delegate void EventHandler<TEventArgs>(object sender, TEventArgs e);
}
Does anyone if there is an awaitable event handler available? E.g.
public delegate Task EventHandlerAsnyc<TEventArgs>(object sender, TEventArgs e);
Thx
If you want your event to be processed async (meaning you can use await to return early and resume later) you can simply declare the handler as async void:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponents();
myButton.Click += myButton_Click;
}
public async void myButton_Click(object sender, EventArgs e)
{
myButton.Enabled = false;
await SomeAsyncOrLongRunningOnAnotherThreadTask();
myButton.Enabled = true;
}
}
This way SomeAsyncOrLongRunningOnAnotherThreadTask() won't block your UI thread. And the handler is resumed after that task completes.
Side note: normally async methods should always return a Task or Task<T> that can be awaited or otherwise handled by the caller. The use case above is (afaik) the only justified case where void should be used for an async method.
The downside of just using async void handlers is that there is no way for the caller to wait for the result. This may be an issue for some interactive event handlers, like the ones using CancelEventArgs.
But you can still declare a Task-returning delegate type, if you wish. You just have to be careful how you raise it then. For instance, you could make an extension method which you can call as handler.Raise(sender, EventArgs.Empty).
public delegate Task EventHandlerAsnyc<TEventArgs>(object sender, TEventArgs eventArgs);
public static async Task Raise<TEventArgs>(this EventHandlerAsnyc<TEventArgs> handlers, object sender, TEventArgs eventArgs)
{
if (handlers == null)
return;
foreach (var handler in handlers.GetInvocationList())
await ((EventHandlerAsnyc<TEventArgs>)handler).Invoke(sender, eventArgs);
}
Alternatively, you can allow handlers to execute concurrently. But this would probably be a bad idea unless well-documented, as it's probably considered surprising behavior.
public static Task RaiseAllowConcurrent<TEventArgs>(this EventHandlerAsnyc<TEventArgs> handlers, object sender, TEventArgs eventArgs)
{
if (handlers == null)
return Task.CompletedTask;
var invocationList = handlers.GetInvocationList();
var tasks = new Task[invocationList.Length];
for (var i = 0; i < invocationList.Length; ++i)
tasks[i] = ((EventHandlerAsnyc<TEventArgs>)invocationList[i]).Invoke(sender, eventArgs);
return Task.WhenAll(tasks);
}

Return in method only when outside event handler has been called

So I want to return in a method only when some asnyc processes (but not with await) has been finshed. What is the best way to block the method procedure and waitint for the event handler. Yes I can do a while loop to block the method and wait for a boolean whichh will be switched when event handler has been called. But I think this can not be the best way?
Maybe you're looking for ManualResetEventSlim:
public class ManualResetEventPlayground
{
public ManualResetEventPlayground()
{
SomeEvent += (sender, e) =>
{
// Opens the door, blocked code will resume
Console.WriteLine("Opening the door to let the method return...");
_resetEvent.Set();
};
Task.Run(() => MethodThatMustWaitUntilSomeEventIsFired());
Task.Run(() => MethodThatFiresTheEvent());
}
private event EventHandler SomeEvent;
private static readonly ManualResetEventSlim _resetEvent = new ManualResetEventSlim(false);
public string MethodThatMustWaitUntilSomeEventIsFired()
{
// Some stuff to do before blocking
try
{
// This will block this thread until
// the event is fired and opens the door
Console.WriteLine("Blocking the thread calling the method");
_resetEvent.Wait();
}
finally
{
_resetEvent.Reset();
}
Console.WriteLine("Now this method can be returned!");
return "finished";
}
public void MethodThatFiresTheEvent()
{
Console.WriteLine("Firing the event...");
if (SomeEvent != null) SomeEvent(this, new EventArgs());
}
}
If you instantiate this class on a Console application you'll get the following output:
The async Task methods will return you a Task<Result> object and you can simply call task.Wait();.
var task = new System.Net.WebClient().DownloadStringTaskAsync("http://google.com");
task.Wait();
Console.WriteLine(task.Result);
Well if you really don't want to use async/await then Monitor is your choice.
In the main thread:
// To wait for a signal from another thread
Monitor.Wait(SyncRoot);
In working thread:
Monitor.Pulse(SyncRoot);
SyncRoot is any object.

Handling moment when async CoreDispatcher work completed or cancelled

For example, I need to use CoreDispatcher for refreshing MVVM properties in the UI Thread.
private void ButtonClick(object sender, RoutedEventArgs e)
{
//Code not compile without keyword async
var dispatcherResult = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
//This method contains awaitable code
await _scanner.ScanAsync();
}
);
dispatcherResult.Completed = new AsyncActionCompletedHandler(TaskInitializationCompleted);
}
private void TaskInitializationCompleted (IAsyncAction action, AsyncStatus status )
{
//Do something...
}
I am expect, then TaskInitializationCompleted handler will fire AFTER ScanAsync method completed, but it fire immediatly after Dispatcher.RunAsync method started and also BEFORE then ScanAsync was completed.
How I can check to really handle async Dispatcher work completed or cancelled?
Instead of registering to the Completed event, you can await RunAsync (Because DispatcherOperation is an awaitable) which will guarantee any code runs only after completion the invocations completion:
private async void ButtonClick(object sender, RoutedEventArgs e)
{
var dispatcherResult = await this.Dispatcher
.RunAsync(CoreDispatcherPriority.Normal,
async () =>
{
await _scanner.ScanAsync();
});
// Do something after `RunAsync` completed
}

Is it possible to have async methods as callbacks to eventhandlers in c#?

My design is illustrated by below example. Having a while true loop doing something and notifying by an event that it has done something to all subscribers. My application should not continue its execution before its done notifying all subscribers, where this works as long as someone do not put a async void on the callback.
If someone put a async void on the callback to await some task, then my loop can continue before the callback is completed. What other designs can I do to avoid this situation.
Its 3th party plugins that register themeself and subscribe to the event, so I have no control over if they put a async void. Understandable I cant do Task callbacks for the EventHandler, so what alternatives do I have with .net 4.5.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication4
{
public class Test
{
public event EventHandler Event;
public void DoneSomething()
{
if (Event != null)
Event(this,EventArgs.Empty);
}
}
class Program
{
static void Main(string[] args)
{
var test = new Test();
test.Event += test_Event;
test.Event +=test_Event2;
while(true)
{
test.DoneSomething();
Thread.Sleep(1000);
}
}
private static void test_Event2(object sender, EventArgs e)
{
Console.WriteLine("delegate 2");
}
static async void test_Event(object sender, EventArgs e)
{
Console.WriteLine("Del1gate 1");
await Task.Delay(5000);
Console.WriteLine("5000 ms later");
}
}
}
If someone put a async void on the callback to await some task, then my loop can continue before the callback is completed. What other designs can I do to avoid this situation.
There is really no way to avoid this. Even if you were to somehow "know" that the subscriber wasn't implemented via async/await, you still couldn't guarantee that the caller didn't build some form of asynchronous "operation" in place.
For example, a completely normal void method could put all of its work into a Task.Run call.
My application should not continue its execution before its done notifying all subscribers
Your current version does follow this contract. You're notifying the subscribers synchronously - if a subscriber does something asynchronously in response to that notification, that is something outside of your control.
Understandable I cant do Task callbacks for the EventHandler, so what alternatives do I have with .net 4.5.
Note that this is actually possible. For example, you can rewrite your above as:
public class Program
{
public static void Main()
{
var test = new Test();
test.Event += test_Event;
test.Event +=test_Event2;
test.DoneSomethingAsync().Wait();
}
}
public delegate Task CustomEvent(object sender, EventArgs e);
private static Task test_Event2(object sender, EventArgs e)
{
Console.WriteLine("delegate 2");
return Task.FromResult(false);
}
static async Task test_Event(object sender, EventArgs e)
{
Console.WriteLine("Del1gate 1");
await Task.Delay(5000);
Console.WriteLine("5000 ms later");
}
public class Test
{
public event CustomEvent Event;
public async Task DoneSomethingAsync()
{
var handler = this.Event;
if (handler != null)
{
var tasks = handler.GetInvocationList().Cast<CustomEvent>().Select(s => s(this, EventArgs.Empty));
await Task.WhenAll(tasks);
}
}
}
You can also rewrite this using event add/remove, as suggested by svick:
public class Test
{
private List<CustomEvent> events = new List<CustomEvent>();
public event CustomEvent Event
{
add { lock(events) events.Add(value); }
remove { lock(events) events.Remove(value); }
}
public async Task DoneSomething()
{
List<CustomEvent> handlers;
lock(events)
handlers = this.events.ToList(); // Cache this
var tasks = handlers.Select(s => s(this, EventArgs.Empty));
await Task.WhenAll(tasks);
}
}
My application should not continue its execution before its done notifying all subscribers, where this works as long as someone do not put a async void on the callback.
I have a blog entry on designing for async event handlers. It is possible to use Task-returning delegates or to wrap an existing SynchronizationContext within your own (which would allow you to detect and wait for async void handlers).
However, I recommend you use "deferrals", which are objects designed specifically to solve this problem for Windows Store applications. A simple DeferralManager is available in my AsyncEx library.
Your event args can define a GetDeferral method as such:
public class MyEventArgs : EventArgs
{
private readonly DeferralManager deferrals = new DeferralManager();
... // Your own constructors and properties.
public IDisposable GetDeferral()
{
return deferrals.GetDeferral();
}
internal Task WaitForDeferralsAsync()
{
return deferrals.SignalAndWaitAsync();
}
}
And you can raise an event and (asynchronously) wait for all asynchronous handlers to complete like this:
private Task RaiseMyEventAsync()
{
var handler = MyEvent;
if (handler == null)
return Task.FromResult<object>(null); // or TaskConstants.Completed
var args = new MyEventArgs(...);
handler(args);
return args.WaitForDeferralsAsync();
}
The benefit of the "deferral" pattern is that it is well-established in the Windows Store APIs, so it's likely to be recognized by end users.

Categories