This question already has answers here:
Should I avoid 'async void' event handlers?
(4 answers)
Closed 2 years ago.
I am getting myself confused about async void and button handlers. I heard in a video tutorial that it was not wise to use async void. I have reviewed these questions:
question
question
In many answers it does actually use this syntax. Thus my confusion. 🧐
Here is my handler:
async private void InstallStyleButton_Clicked(object sender, EventArgs e)
{
var customFileType =
new FilePickerFileType(new Dictionary<DevicePlatform, IEnumerable<string>>
{
{DevicePlatform.macOS, new[] {"xsl"} }
});
var pickResult = await FilePicker.PickAsync(new PickOptions
{
FileTypes = customFileType,
PickerTitle = "Select template to install"
});
if(pickResult != null)
{
// Build target path
string targetFile = Path.Combine("/Users/Shared/VisitsRota.MacOS/Styles", pickResult.FileName);
// OK to install?
if(!File.Exists(targetFile))
{
// Install
File.Copy(pickResult.FullPath, targetFile);
// Add it
StylesPicker.ItemsSource.Add(pickResult.FileName);
// Select it
StylesPicker.SelectedItem = pickResult.FileName;
}
}
}
I realise it is not a good idea to hardcode the folder path and I will eventually get around to that. Other than that, my handler appears to operate fine on my development MacBook Pro when I try it.
Is my use of async void with the button handler ok? And if not, how should my handler be adjusted?
I found this link where it states:
To summarize this first guideline, you should prefer async Task to async void. Async Task methods enable easier error-handling, composability and testability. The exception to this guideline is asynchronous event handlers, which must return void.
So my code is OK.
Related
I'm trying to figure out how to refresh the client-side component after button click.
Repo Link with example: https://github.com/ovie91/RefreshComponent
Site /test or from nav menu test
So I have OnInitializedAsync method that is retrieving data from API
protected override async Task OnInitializedAsync()
{
result = await (some API Call);
}
Then I have a method connected to the button
private async void ButtonClick()
{
await (some API Call);
result = null;
this.StateHasChanged(); <--- Doesnt work :<
}
I have tried to use this.StateHasChanged(); but there is no reaction.
As a workaround, I can force you to navigate again to the same website but this refresh "Whole" website but not a component.
Any ideas on how to deal with it?
whole code (stripped to minimum):
#page "/test"
#inject HttpClient Http
#if (result == null)
{
<p>Loading...<p>
}
else
{
#result
<button #onclick="(() => ButtonClick())">Click</button>
}
#code {
private APIObject result;
protected override async Task OnInitializedAsync()
{
result = await (some API Call);
}
private async void ButtonClick()
{
await (some API Call);
result = null;
this.StateHasChanged(); <--- Doesnt work :<
}
}
Update
I want to refresh component so OnInitializedAsync would be triggered again and that would mean I don't have to run the same code again after button click. Hope you understand what I mean.
To get the desired output you just have to shuffle the lines a little, from:
private async void ButtonClick()
{
await (some API Call); // UI checks if an update is needed (No)
result = null; // now an update is needed
this.StateHasChanged(); <--- Doesnt work :< // actually: not needed
}
to:
private async Task ButtonClick()
{
result = null; // change the state
//this.StateHasChanged(); // not needed, a request is pending
await (some API Call); // should show '<h3>Loading</h3>' now
}
Note that the UI is updated when an await releases the Thread.
however, from your answer we get
var APICall = await Http.GetAsync("SomeAPI");
Thread.Sleep(2000);
This should work when Http.GetAsync("SomeAPI"); really is an async call and not just some stand-in pseudo code. Because Thread.Sleep(2000); will really freeze things.
If you want to make sure:
private async Task GetData()
{
await Task.Delay(1); // release the thread for rendering
var APICall = await Http.GetAsync("SomeAPI");
Random rnd = new Random();
Thread.Sleep(2000); // Task.Delay() is much preferred
result = "Random Number: " + rnd.Next();
}
Thread.Sleep() is appropriate to simulate some CPU (not I/O) intensive code. So I'm not saying it's wrong but be aware of the difference.
And it is much better to make eventhandlers async Task instead of async void but that is not the direct problem here.
From here:
Blazor uses a synchronization context (SynchronizationContext) to enforce a single logical thread of execution. A component's lifecycle methods and any event callbacks that are raised by Blazor are executed on the synchronization context.
Blazor Server's synchronization context attempts to emulate a single-threaded environment so that it closely matches the WebAssembly model in the browser, which is single threaded. At any given point in time, work is performed on exactly one thread, giving the impression of a single logical thread. No two operations execute concurrently.
So as enet asnwered, you should use async Task signature instead of async void.
I have moved API call to another Method and inside of OnInitializedAsync I called it.
Then when I reset the result variable to see Loading state I'm able to "refresh" component to achieve that you need to add. this.StateHasChanged()
Now I have a responsive component to updates that are happening :)
#page "/test"
#using System.Threading;
#inject HttpClient Http
#if (result == null)
{
<h3>Loading</h3>
}
else
{
#result
<button #onclick="(() => ButtonClick())">Click</button>
}
#code {
private string result;
protected override async Task OnInitializedAsync()
{
await GetData();
}
private async Task GetData()
{
var APICall = await Http.GetAsync("SomeAPI");
Random rnd = new Random();
Thread.Sleep(2000);
result = "Random Number: " + rnd.Next();
}
private async Task ButtonClick()
{
await Http.GetAsync("SomeAPIcall");
result = null; // required to see loading state.
this.StateHasChanged(); // when added model is refreshed and Loading state is visible.
await GetData();
}
}
I want to link async method to a delegate command in prism framework in Xamarin.Forms and my question is how to do it?
Is below solution correct? Is there exist any pitfall? (deadlock, UI slow or freezing, bad practices, ...)
{ // My view model constructor
...
MyCommand = new DelegateCommand(async () => await MyJobAsync());
...
}
private async Task MyJobAsync()
{
... // Some await calls
... // Some UI element changed such as binded Observable collections
}
You can use async void directly. However, a few notes from my experience...
The structure of your code is: start asynchronous operation and then update UI with the results. This implies to me that you would be better served with a NotifyTask<T> kind of approach to asynchronous data binding, not commands. See my async MVVM data binding article for more about the design behind NotifyTask<T> (but note that the latest code has a bugfix and other enhancements).
If you really do need an asynchronous command (which is much more rare), you can use async void directly or build an async command type as I describe in my article on async MVVM commmands. I also have types to support this but the APIs for these are more in flux.
If you do choose to use async void directly:
Consider making your async Task logic public, or at least accessible to your unit tests.
Don't forget to handle exceptions properly. Just like a plain DelegateTask, any exceptions from your delegate must be properly handled.
Just have a look at this link if you're using Prism Library: https://prismlibrary.com/docs/commands/commanding.html#implementing-a-task-based-delegatecommand
In case you want to pass a CommandParameter to DelegateCommand, use in the DelegateCommand variable declaration this syntax
public DelegateCommand<object> MyCommand { get; set; }
In the constructor of the ViewModel initialize it this way:
MyCommand = new DelegateCommand<object>(HandleTap);
where HandleTap is declared as
private async void HandleTap(object param)
Hope it helps.
As has already been mentioned the way to handle async code with delegate command is to use async void. There has been a lot of discussion on this, far beyond just Prism or Xamarin Forms. The bottom line is that ICommand that both the Xamarin Forms Command and Prism DelegateCommand are limited by ICommand's void Execute(object obj). If you'd like to get more information on this I would encourage you to read the blog by Brian Lagunas explaining why DelegateCommand.FromAsync handler is obsolete.
Generally most concerns are handled very easily by updating the code. For example. I often hear complaints about Exceptions as "the reason" why FromAsync was necessary, only to see in their code they never had a try catch. Because async void is fire and forget, another complaint I've heard is that a command could execute twice. That also is easily fixed with DelegateCommands ObservesProperty and ObservesCanExecute.
I think the two main problems when calling an asynchronous method from one that executes synchronously (ICommand.Execute) are 1) denying to execute again while previous call is still running 2) handling of exceptions. Both can be tackled with an implementation like the following (prototype). This would be an async replacement for the DelegateCommand.
public sealed class AsyncDelegateCommand : ICommand
{
private readonly Func<object, Task> func;
private readonly Action<Exception> faultHandlerAction;
private int callRunning = 0;
// Pass in the async delegate (which takes an object parameter and returns a Task)
// and a delegate which handles exceptions
public AsyncDelegateCommand(Func<object, Task> func, Action<Exception> faultHandlerAction)
{
this.func = func;
this.faultHandlerAction = faultHandlerAction;
}
public bool CanExecute(object parameter)
{
return callRunning == 0;
}
public void Execute(object parameter)
{
// Replace value of callRunning with 1 if 0, otherwise return - (if already 1).
// This ensures that there is only one running call at a time.
if (Interlocked.CompareExchange(ref callRunning, 1, 0) == 1)
{
return;
}
OnCanExecuteChanged();
func(parameter).ContinueWith((task, _) => ExecuteFinished(task), null, TaskContinuationOptions.ExecuteSynchronously);
}
private void ExecuteFinished(Task task)
{
// Replace value of callRunning with 0
Interlocked.Exchange(ref callRunning, 0);
// Call error handling if task has faulted
if (task.IsFaulted)
{
faultHandlerAction(task.Exception);
}
OnCanExecuteChanged();
}
public event EventHandler CanExecuteChanged;
private void OnCanExecuteChanged()
{
// Raising this event tells for example a button to display itself as "grayed out" while async operation is still running
var handler = CanExecuteChanged;
if (handler != null) handler(this, EventArgs.Empty);
}
}
async void
I personally would avoid "async void" at all cost. It is impossible to know from the outside when the operation has finished and error handling becomes tricky. In regards to latter, for instance writing an "async Task" method which is called from an "async void" method almost needs to be aware of how its failing Task is propagated:
public async Task SomeLogic()
{
var success = await SomeFurtherLogic();
if (!success)
{
throw new DomainException(..); // Normal thing to do
}
}
And then someone writing on a different day:
public async void CommandHandler()
{
await SomeLogic(); // Calling a method. Normal thing to do but can lead to an unobserved Task exception
}
Is UI thread running DelegateCommand and background threads running await expression?
Yes, the UI thread runs the DelegateCommand. In case of an async one, it runs until the first await statement, and then resumes his regular UI thread work. If the awaiter is configured to capture the synchronization context (that is, you do not use .ConfigureAwait(false)) the UI thread will continue to run the DelegateCommand after the await.
Is UI thread running DelegateCommand and background threads running await expression?
Whether the "await expression" runs on a background thread, foreground thread, a threadpool thread or whatever depends on the api you call. For example, you can push cpu-bound work to the threadpool using Task.Run or you can wait for an i/o-operation without using any thread at all with methods like Stream.ReadAsync
public ICommand MyCommand{get;set;}
//constructor
public ctor()
{
MyCommand = new Xamarin.Forms.Command(CmdDoTheJob);
}
public async void DoTheJob()
{
await TheMethod();
}
public DelegateCommand MyCommand => new DelegateCommand(MyMethod);
private async void MyMethod()
{
}
There are no pitfalls. A void return type in async method was created especially for delegates. If you want to change something, that has reflected on UI, insert relevant code in this block:
Device.BeginOnMainThread(()=>
{
your code;
});
Actually, ICommand and DelegateCommand pretty similar, so an above answer is quite right.
I cannot seem to find a good solution for this issue online. I have a device that is running Windows Embedded Handheld 6.5. I run the solution located at below
C:\Program Files (x86)\Windows Mobile 6.5.3 DTK\Samples\PocketPC\CS\GPS
I deploy the code to my device, not an emulator, and the code breaks with a null reference exception at
Invoke(updateDataHandler);
The solution ive seen recommends changing this to below
BeginInvoke(updateDataHandler);
But now the code breaks at Main with NullRefreceException.
Application.Run(new Form1());
Has anyone found a solution for this?
Did you alter the code? updateDataHandler is initialized in Form_Load:
private void Form1_Load(object sender, System.EventArgs e)
{
updateDataHandler = new EventHandler(UpdateData);
so that object will not be NULL. But there are other annoyances with the code, especially the Samples.Location class. You may instead use http://www.hjgode.de/wp/2010/06/11/enhanced-gps-sample-update/ as a starting point and the older one: http://www.hjgode.de/wp/2009/05/12/enhanced-gps-sampe/
The main issue with the sample is that it does not use a callback (delegate) to update the UI. If an event handler is fired from a background thread, the handler can not directly update the UI. Here is what I always use to update the UI from a handler:
delegate void SetTextCallback(string text);
public void addLog(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.txtLog.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(addLog);
this.Invoke(d, new object[] { text });
}
else
{
txtLog.Text += text + "\r\n";
}
}
I'm unsure about the best practice for obtaining and updating input received from a controller monitored using the GamePad class in UWP.
I've seen a couple of examples of people using Dispatch Timers and async loops inside the GamePadAdded event. In Win32 applications, I would have handled input in the WinMain update/message loop, but in UWP apps I don't know of anything similar.
Is there a loop in UWP apps that input should be collected/handled like in Win32 apps? What is the recommended protocol for polling for input from a input device (nominally a Xbox One controller)?
I'm happy to read more about UWP app development but I'm unsure of any guides that reference something like this.
Edit: It would be productive if, instead of downvoting and moving on, you shared thoughts on why this question deserved a downvote.
I've seen a couple of examples of people using Dispatch Timers and async loops inside the GamePadAdded event
This is the right way in UWP app to read Gamepad data. A little suggestion is, move the loop reading part on UI thread if you need to update UI frequently. See the solution in this blog
Is there a loop in UWP apps that input should be collected/handled like in Win32 apps
You may make a wrapper with custom event, see the open source implementation: XBoxGamepad
public class XBoxGamepad
{
private List<Gamepad> _controllers = new List<Gamepad>();
private bool _running = true;
Task backgroundWorkTask;
public event EventHandler<GamepadButtons> OnXBoxGamepadButtonPressA;
//omitted......
public XBoxGamepad()
{
Gamepad.GamepadAdded += Gamepad_GamepadAdded;
Gamepad.GamepadRemoved += Gamepad_GamepadRemoved;
backgroundWorkTask = Task.Run(() => PollGamepad());
}
//omitted......
private void Start()
{
_running = true;
}
public void Stop()
{
_running = false;
}
public async Task PollGamepad()
{
while (true)
{
if (_running)
{
foreach (Gamepad controller in _controllers)
{
if (controller.GetCurrentReading().Buttons == GamepadButtons.A)
{
OnXBoxGamepadButtonPressA(controller, controller.GetCurrentReading().Buttons);
}
//omitted......
}
}
await Task.Delay(50);
}
}
private void Gamepad_GamepadRemoved(object sender, Gamepad e)
{
_controllers.Remove(e);
}
private void Gamepad_GamepadAdded(object sender, Gamepad e)
{
_controllers.Add(e);
}
}
So i don't actually have a question because i've already solved it, but in case someone else runs into this issue it's always nice to have a neat solution.
And while there is a plentitude of "Can't create handler inside thread which has not called Looper.prepare()" - questions there is none tagged with xamarin. (so theirs is all java and i had 0 matches for "Can't create handler inside thread which has not called Looper.prepare() [xamarin]")
The issue is generated because You tried to do work on UI from other thread. If you want to change UI, Must call UI changes from UI thread.
Xamarin Android:
activity.RunOnUiThread(() =>
{
// Write your code here
});
Xamarin IOS:
nsObject.BeginInvokeOnMainThread(() =>
{
// Write your code here
});
Xamarin Forms:
Device.BeginInvokeOnMainThread(() =>
{
// Write your code here
});
public static class PageExtensions
{
public static Task<bool> DisplayAlertOnUi(this Page source, string title, string message, string accept, string cancel)
{
TaskCompletionSource<bool> doneSource = new TaskCompletionSource<bool>();
Device.BeginInvokeOnMainThread(async () =>
{
try
{
var result = await source.DisplayAlert(title, message, accept, cancel);
doneSource.SetResult(result);
}
catch (Exception ex)
{
doneSource.SetException(ex);
}
});
return doneSource.Task;
}
}
Finally i had a case for using TaskCompletionSource to solve an issue.
Very similar actually happend to me. Previous evening I was developing and testing my app. Next morning, from other computer I got the exception you described. I was remembered from a official Xamarin.Forms documentation that sometimes a bin and obj folder removal solves lot of issues.
I did exactly the same, removed my bin and obj folder from my shared Xamarin.Forms library and also from Xamarin.Android library.
The strange exception disappeared.