The callback for a timer returns a value (bool, whether to repeat). You can't make a lambda that returns a value async. Instead you can return false (stop), use ContinueWith instead of await, and then in the continuation callback start a new timer if you need to.
Could you explain more with an example? I am doing a background downloading/uploading process which is calling my web services. However I need to do this on a timer once a user logs into the app. In order to keep my web services from slowing down the UI, I would like it to call them asynchronously every 30 seconds to a minute. I haven't found a way to have this work besides calling my methods synchronously using the Device.StartTimer.
Thanks for any help or direction you may be able to give.
private void ScheduleWebRequest()
{
// Start a timer that runs after 1 minute.
Device.StartTimer(TimeSpan.FromMinutes(1), () =>
{
Task.Factory.StartNew(async () =>
{
// Do the actual request and wait for it to finish.
await PerformWebRequest();
// Switch back to the UI thread to update the UI
Device.BeginInvokeOnMainThread(() =>
{
// Update the UI
// ...
// Now repeat by scheduling a new request
ScheduleWebRequest();
});
});
// Don't repeat the timer (we will start a new timer when the request is finished)
return false;
});
}
That uses the Device class, but you could also try a pure async/await method:
private async void PollWebRequest()
{
while (_keepPolling)
{
await PerformWebRequest();
// Update the UI (because of async/await magic, this is still in the UI thread!)
if (_keepPolling)
{
await Task.Delay(TimeSpan.FromMinutes(1));
}
}
}
You could also make this cancellable by using a CancellationToken in the call to Task.Delay. There is a very good blog series here about async/await that also covers cancellation. It would be good for you to read through that.
Thank you so much for the example and the blog. I will definitely take a look at it but it does seem like I will be able to utilize the first example you gave me.
My experience was that returning false from the callback stops the callback from being called, but the timer never works normally again after that, appears to call callback multiple times for each timer tick, one for each time I have called Device.StartTimer. I modified my code to only call this method once. Something not right here. Break points in the callback do not get hit after returning false, but if I call Device.StartTime again, I get two callbacks for each interval. In essence, the callback collection gets added to but never emptied?
private void ScheduleWebRequest()
{
// Start a timer that runs after 1 minute.
Device.StartTimer(TimeSpan.FromMinutes(1), () =>
{
Task.Factory.StartNew(async () =>
{
// Do the actual request and wait for it to finish.
await PerformWebRequest();
// Switch back to the UI thread to update the UI
Device.BeginInvokeOnMainThread(() =>
{
// Update the UI
// ...
// Now repeat by scheduling a new request
ScheduleWebRequest();
});
});
// Don't repeat the timer (we will start a new timer when the request is finished)
return false;
});
}
That uses the Device class, but you could also try a pure async/await method:
private async void PollWebRequest()
{
while (_keepPolling)
{
await PerformWebRequest();
// Update the UI (because of async/await magic, this is still in the UI thread!)
if (_keepPolling)
{
await Task.Delay(TimeSpan.FromMinutes(1));
}
}
}
You could also make this cancellable by using a CancellationToken in the call to Task.Delay. There is a very good blog series here about async/await that also covers cancellation. It would be good for you to read through that.
very old question, i hope you remember but why did you use Task.Factory.StartNew together with Device.BeginInvokeOnMainThread ? i thought that they roughly do similar work to run on a separate thread? wouldnt it be sufficient to use only Device.BeginInvokeOnMainThread?
@batmaci said:
why did you use Task.Factory.StartNew together with Device.BeginInvokeOnMainThread ? i thought that they roughly do similar work to run on a separate thread? wouldnt it be sufficient to use only Device.BeginInvokeOnMainThread?
Task.Factory.StartNew starts a new Task. Device.BeginInvokeOnMainThread invokes an action on the main (UI) thread.
Work on the basis that Task.Factory.StartNew does not run things on the UI thread. If having started a Task, if something that Task does will want to interact with the UI, it's necessary to use Device.BeginInvokeOnMainThread to ensure that the UI interaction happens on the UI thread.
Whether Device.StartTimer runs things on the UI thread is an interesting question. Whilst the current official documentation for Device.StartTimer says "Starts a recurring timer on the UI thread using the device clock capabilities", there are plenty of blogs (including by MVPs) etc on the web saying that Device.StartTimer does not run things on the UI thread and so UI interactions happening in the invoked code will require Device.BeginInvokeOnMainThread. I've asked for clarification on this recently, but I don't think I've had an official answer.
@JohnHardman "it's necessary to use Device.BeginInvokeOnMainThread to ensure that the UI interaction happens on the UI thread."
quick question regarding this. i understand the concept using in xaml.cs like in the official documentation but when using mvvm, when to use Device.BeginInvokeOnMainThread? should we use it only when we raise propertychanged to update UI bindings? or it isnt meant to be used in viewmodel
@batmaci said: @JohnHardman "it's necessary to use Device.BeginInvokeOnMainThread to ensure that the UI interaction happens on the UI thread."
quick question regarding this. i understand the concept using in xaml.cs like in the official documentation but when using mvvm, when to use Device.BeginInvokeOnMainThread? should we use it only when we raise propertychanged to update UI bindings? or it isnt meant to be used in viewmodel
If your View Model is doing work on a thread other than the UI thread and that work involves raising PropertyChanged, then yes you should use Device.BeginInvokeOnMainThread when raising PropertyChanged.
In your Constructor
try
{
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
Reloj();
return true;
});
}
catch{}
You'll want to re-organise the try/catch in that code to be inside the StartTimer block. Currently, if Reloj() were to throw an exception, your catch block won't catch it, and your app will terminate.
Posts
The callback for a timer returns a value (bool, whether to repeat). You can't make a lambda that returns a value async. Instead you can return false (stop), use ContinueWith instead of await, and then in the continuation callback start a new timer if you need to.
Adam,
Could you explain more with an example? I am doing a background downloading/uploading process which is calling my web services. However I need to do this on a timer once a user logs into the app. In order to keep my web services from slowing down the UI, I would like it to call them asynchronously every 30 seconds to a minute. I haven't found a way to have this work besides calling my methods synchronously using the Device.StartTimer.
Thanks for any help or direction you may be able to give.
Something like this:
That uses the Device class, but you could also try a pure async/await method:
You could also make this cancellable by using a
CancellationToken
in the call toTask.Delay
. There is a very good blog series here about async/await that also covers cancellation. It would be good for you to read through that.Thank you so much for the example and the blog. I will definitely take a look at it but it does seem like I will be able to utilize the first example you gave me.
My experience was that returning false from the callback stops the callback from being called, but the timer never works normally again after that, appears to call callback multiple times for each timer tick, one for each time I have called Device.StartTimer. I modified my code to only call this method once. Something not right here. Break points in the callback do not get hit after returning false, but if I call Device.StartTime again, I get two callbacks for each interval. In essence, the callback collection gets added to but never emptied?
@WinSomeLoseSome - if that's the case, can you log a bug for that at bugzilla.xamarin.com if nobody else has already done so.
very old question, i hope you remember but why did you use Task.Factory.StartNew together with Device.BeginInvokeOnMainThread ? i thought that they roughly do similar work to run on a separate thread? wouldnt it be sufficient to use only Device.BeginInvokeOnMainThread?
Task.Factory.StartNew
starts a new Task.Device.BeginInvokeOnMainThread
invokes an action on the main (UI) thread.Work on the basis that
Task.Factory.StartNew
does not run things on the UI thread. If having started a Task, if something that Task does will want to interact with the UI, it's necessary to useDevice.BeginInvokeOnMainThread
to ensure that the UI interaction happens on the UI thread.Whether
Device.StartTimer
runs things on the UI thread is an interesting question. Whilst the current official documentation forDevice.StartTimer
says "Starts a recurring timer on the UI thread using the device clock capabilities", there are plenty of blogs (including by MVPs) etc on the web saying thatDevice.StartTimer
does not run things on the UI thread and so UI interactions happening in the invoked code will requireDevice.BeginInvokeOnMainThread
. I've asked for clarification on this recently, but I don't think I've had an official answer.@JohnHardman "it's necessary to use Device.BeginInvokeOnMainThread to ensure that the UI interaction happens on the UI thread."
quick question regarding this. i understand the concept using in xaml.cs like in the official documentation but when using mvvm, when to use Device.BeginInvokeOnMainThread? should we use it only when we raise propertychanged to update UI bindings? or it isnt meant to be used in viewmodel
If your View Model is doing work on a thread other than the UI thread and that work involves raising PropertyChanged, then yes you should use
Device.BeginInvokeOnMainThread
when raising PropertyChanged.Hello,
This thread is not clear at all for me.
I use the documentation described there:
https://docs.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xaml-basics/data-bindings-to-mvvm
But I need to show time and geolocation. So I need to call an async method in a Device.StartTimer.
Something looking like that but it doesn't work:
Is the first example in this thread the solution? Is it ok with MVVM? who will start a new Device.StarTimer if the method returns False?
What do you mean by "doesn't work"? it locks? it never gets executed? it throws an exception?
Also never use .Result on a task, it defeats the whole purpose of async programming as it will run the task synchronously.
@Amar_Bait I am sorry, I did not see your answer. You are right: the issue is dot Result that freezes the app as jamesmontemagno tells here:
https://github.com/xamarin/Essentials/issues/868
and here:
https://montemagno.com/c-sharp-developers-stop-calling-dot-result/
@adamkemp this is awesome:
http://www.jeremybytes.com/downloads.aspx
Hi @AlexandreReyesMartins
try this, is just an alternative
XAML
C#
//--------------------------
// in your Xamarin.Forms app you will have a chronometer like this

@LuisDavidDelaCruz said:
You'll want to re-organise the try/catch in that code to be inside the StartTimer block. Currently, if Reloj() were to throw an exception, your catch block won't catch it, and your app will terminate.