Xamarin, wait for async function ends - xamarin

In main file MainPage I have method OnAppearing contains two functions.
MainPage.cs
protected override void OnAppearing()
{
DataBaseService.checkNoteAlarmTimeActive();
this.loadNotes();
base.OnAppearing();
}
Unfortunately method loadNotes() is fired before method DataBaseService.checkNoteAlarmTimeActive(); ends. It makes some problem in my app.
How to change it to this.loadNotes() wait to previous function ends job?
Thank you.
Class DataBaseService with metod:
public static async void checkNoteAlarmTimeActive()
{
List<Note> notes = await getTimeActiveNotes();
foreach(Note note in notes)
{
if(note.AlarmTime < DateTime.Now)
{
note.AlarmTimeActive = false;
updateRecord(note);
}
}
}
public static async Task<List<Note>> getTimeActiveNotes()
{
var notes = await _dbConnect.Table<Note>().Where(i => i.AlarmTimeActive == true).ToListAsync();
return notes;
}

The short answer is that you add async and await.
protected async override void OnAppearing()
{
await DataBaseService.checkNoteAlarmTimeActive();
this.loadNotes();
base.OnAppearing();
}

Your CheckNoteAlarmTimeActive()method returns void, therefore it cannot be awaited reliably (it is basically fire and forget).
You should always return Task instead.
The last thing to do is to add the async keyword to OnApprearing, so you can use await inside there (like Sean Sparkman said):
//C#
protected async override void OnAppearing()
{
await DataBaseService.checkNoteAlarmTimeActive();
this.loadNotes();
base.OnAppearing();
}
Just keep in mind: As OnAppearing() also returns void, it will return to the caller immediately after something is awaited in there (in this case your database call). So the rest of the lifecycle will continue and the rest of the method (after the await) will only happen after it finished.

Related

Blazor client side refresh component

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();
}
}

Xamarin Wrong Count of NavigationStack in OnAppearing after Navigatig Back

I am using the following method to set the pagenumber of my current page:
protected override void OnAppearing()
{
base.OnAppearing();
// Page number
_pageNumber = Navigation.NavigationStack.Count - 2;
}
Everything works fine, if I am navigating forward and pushing a new page onto the stack like this:
private async void btnContinue_Clicked(object sender, EventArgs e)
{
// Proceed to next view
await Navigation.PushAsync(new MainPage());
}
However, when I am navigating back from one page, the OnAppearing() method seems to be called before the previous page is popped. Thus, the _pageNumber variable will effectively be the one of the previous page. How do I work around that? I thought of placing it inside the constructor but the NavigationStack.Count always returns 0 in there.
My pages are dynamically programmed, therefore, I cannot hardcode the page number into the page because the same page is called but the values are changing based on the number of the page.
This will be an expected effect .
When the PopAsync method is invoked, the following events occur:
The page calling PopAsync has its OnDisappearing override invoked.
The page being returned to has its OnAppearing override invoked.
The PopAsync task returns.
So if you want to get current count of NavigationStack , you could improve the answer like following :
In the demo , I used MessageingCenter to pass the current of NavigationStack to last page . And the label will display the current NavigationStack .
bool isFirstLoad = true;
protected override void OnAppearing()
{
base.OnAppearing();
if(isFirstLoad)
{
label.Text = Navigation.NavigationStack.Count.ToString();
isFirstLoad = false;
}
MessagingCenter.Subscribe<Object,int>(this, "pop", (arg, num) => {
label.Text = (num-1).ToString();
});
}
And when you call Pop
private async void Button_Clicked_1(object sender, EventArgs e)
{
MessagingCenter.Send<Object,int>(this, "pop", Navigation.NavigationStack.Count);
await Navigation.PopAsync();
}

Can I change the UI from a method that gets data (not async) that has been called with await?

I have this code:
protected override async void OnAppearing()
{
base.OnAppearing();
Subscribe();
vm.IsBusy = true;
if (Change.firstTimeCardsTab == true)
{
SetButtons(Settings.cc.ShortText());
await Task.Run(() => GetOnAppearingData());
}
}
private void GetOnAppearingData()
{
App.DB.GetData();
AddDetailSection();
SetPageDetails();
Change.firstTimeCardsTab = false;
vm.IsBusy = false;
}
vm.IsBusy = true; shows an activity indicator on the page and vm.IsBusy = false; would I hoped turn it off.
However when I run the code I get a message saying:
UIKit Consistency error: you are calling a UIKit method that can only
be invoked from a UI thread.
Can someone give me advice on how I can set the activity indicator to show, then get the data and then set it to not show? Note that AddDetailSection(); adds some elements to the XAML. Could this be the problem?
UI can be manipulated only from the main thread. There is a method Device.BeginInvokeOnMainThread(Action) that can help to solve your issue, more can be found in the official documentation.
Just wrap all UI related actions by it:
Device.BeginInvokeOnMainThread(() => {
SetPageDetails();
// Any other actions
});

How to use async method in DelegateCommand

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.

How to cancel a running async function in xamarin forms

I called async function in my code , which call rest service and populate a data structure. But somehow i need to cancel that function before its completion , how can i achieve this.
getAdDetails(ad.id,ad.campaign_type);
private async void getAdDetails(int campaign_id, string campaign_type) {
// some code here
}
There is something called "CancelationToken" which is supposed to be for such stuff.
Another way to do so is by throwing an exception when you want to cancel the process .
Another way is by having a flag which can be named "ShouldExecute" , and in the method you keep monitoring it.
I also tend to ignore the results which come from the method when they are not needed and let the thread executes in peace but yet ignored when it comes back.
Assuming you have some background logic in your function:
CancellationTokenSource _cancellation;
public void SomeFunctionToStartDataRefresh(){
_cancellation = new CancellationTokenSource();
try{
getAdDetails(id, type, cancellation.Token);
}catch(OperationCanceledException ex){
//Operation is cancelled
}
}
private async Task getAdDetails(ad.id,ad.campaign_type);
private async void getAdDetails(int campaign_id, string campaign_type, CancellationToken token) {
var data = await fetchDatafromServer()
token.ThrowIfCancellationRequested();
await DosomethingWithData();
token.ThrowIfCancellationRequested();
await DoSomethingElseWithData();
}

Resources