How to cancel a running async function in xamarin forms - android-asynctask

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

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

How to use HttpContext inside Task.Run

There is some posts explain how to tackle, but couldnt help me much..
Logging Request/Response in middleware, it works when use 'await' with Task.Run() but since its awaited current operation to complete there is performance issue.
When I remove await as below, it runs fast but not logging anything, since HttpContext instance not available to use inside parallel thread
public class LoggingHandlerMiddleware
{
private readonly RequestDelegate next;
private readonly ILoggerManager _loggerManager;
public LoggingHandlerMiddleware(RequestDelegate next, ILoggerManager loggerManager)
{
this.next = next;
_loggerManager = loggerManager;
}
public async Task Invoke(HttpContext context, ILoggerManager loggerManager, IWebHostEnvironment environment)
{
_ = Task.Run(() =>
{
AdvanceLoggingAsync(context, _loggerManager, environment);
});
...
}
private void AdvanceLoggingAsync(HttpContext context, ILoggerManager loggerManager, IWebHostEnvironment environment, bool IsResponse = false)
{
{
context.Request.EnableBuffering(); // Throws ExecutionContext.cs not found
result += $"ContentType:{context.Request.ContentType},";
using (StreamReader reader = new StreamReader(context.Request.Body, Encoding.UTF8, true, 1024, true))
{
result += $"Body:{await reader.ReadToEndAsync()}";
context.Request.Body.Position = 0;
}
loggerManager.LogInfo($"Advance Logging Content(Request)-> {result}");
}
How can I leverage Task.Run() performance with accessing HttpContext?
Well, you can extract what you need from the context, build your string you want to log, and then pass that string to the task you run.
However, firing and forgetting a task is not good. If it throws an exception, you risk of bringing down the server, or at least you will have very hard time getting information about the error.
If you are concerned about the logging performance, better add what you need to log to a message queue, and have a process that responds to new messages in the queue and logs the message to the log file.

Xamarin, wait for async function ends

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.

await async code seems to still be running sync

I'm new to async / await, and have been trying to implement it in my 4.6 web api 2 project.
public class MyController : ApiController
{
public async Task<Thing> Search(String searchTerms)
{
myThing = new Thing();
myThing.FirstProperty = await doFirstPropertyAsync(searchTerms);
myThing.SecondProperty = await doSecondPropertyAsync(searchTerms);
return myThing;
}
}
Basically I'm returning a class (Thing) that has two properties that take a few seconds each to populate. I'm actually loading maybe ~10 properties, but it's the same logic for all of them.
public async Task<MyCoolSubObject> doFirstPropertyAsync(string searchTerms)
{
SomeController sController = new SomeController();
Debug.WriteLine("first - starting.");
var x = await Task.Run(()=>sController.Lookup(searchTerms));
Debug.WriteLine("first - finishing.");
return x;
}
public async Task<MyCoolSubObject> doSecondPropertyAsync(string searchTerms)
{
SomeOtherController sController = new SomeOtherController();
Debug.WriteLine("second - starting.");
var x = await Task.Run(()=>sController.Lookup(searchTerms));
Debug.WriteLine("second - finishing.");
return x;
}
What's got my scratching my head:
When I look at the debug outputs, the first property assignment method call starts and finishes before the second completes. Again, I actually have like ten of these and no matter what order I put the property assignments in they complete in a serial fashion (ie: nothing starts until another one finishes).
These property assignments under the hood are basically doing database calls that take a while, hence I wanted them running in parallel if possible. The methods themselves ( SomeController.Lookup(string) ) contain no await/async/task stuff.
Again, I actually have like ten of these and no matter what order I
put the property assignments in they complete in a serial fashion (ie:
nothing starts until another one finishes).
This happens because in your code you use the await keyword as soon as you kickoff the task, by doing that you prevent the method to continue to execute the next statement before the task will be done.
If you want to run your tasks in parallel you should kickoff all of them and only then await all of them using Task.WhenAll:
public async Task<Thing> Search(String searchTerms)
{
myThing = new Thing();
var firstTask = doFirstPropertyAsync(searchTerms);
var secondTask = doSecondPropertyAsync(searchTerms);
await Task.WhenAll(firstTask, secondTask);
myThing.FirstProperty = await firstTask;
myThing.SecondProperty = await secondTask;
return myThing;
}
Note that when we await every task separately after we await Task.WhenAll the tasks have already been done, we do that in order to get the result from the task, although we can use the Result property (it will not block since we know the task has already been done) I prefer to use await for consistency reasons.

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.

Resources