Is there a way for Flutter's Timer.periodic to wait for a function return plus a fixed duration before moving to the next cycle? - flutter

I use Timer.periodic with a duration normally. This works perfectly for my current use case.
Duration fiveSecs = const Duration(seconds: 5);
new Timer.periodic(fiveSecs, checkChange);
void checkChange(Timer timer) async {
//do some network calls
}
In this particular case I make network calls that take no longer than 500ms, and doing them every 5 seconds is enough for whatever depends on what those calls return.
Now I want to check for new values as frequently as 2 seconds, however, based on what needs to be checked a network call could take anywhere from a few hundred milliseconds to even 10 seconds. I could give a large margin and use a frequency of like 20 seconds for the timer, but in this case I would even prefer a 1 second frequency if possible.
So is there a way the timer could wait for a function, and still have a fixed duration like 1 second. So the maximum time it would take would be 1 second + callback runtime? Or is there a better way this could be achieved, I'm open to suggestions. Thank you.

void startTimer() {
_timer = Timer.periodic(Duration(seconds: 1), (Timer t) async {
print("lets wait for 5 seconds");
_timer.cancel();
await Future.delayed(Duration(seconds: 5));
print("Job is done");
print(DateTime.now());
print("Do it again");
startTimer();
});
}

I have encountered the same situation lately,
what I did was (in my case inside a static class) to add a static boolean value and toggle it accoding to my situation needs, later checking it's value inside the Timer.Periodic callback.
Timer.periodic(ConstantValues.socketTimerOperationDelay, (Timer t) async{
if(_udpHandlerCompleted){
_udpHandlerCompleted = false;
if(!_shouldSendBroadcast || _shutDown)
t.cancel();
else
_writeAllToUdpSocket(_udpSocket, data, ConstantValues.udpMulticastGroup, ConstantValues.udpMulticastPort);
_udpHandlerCompleted = true;
}
});
As you can see in my situation, I had to wait for the callback to end before I move to the next one, I believe this is what you're looking for, but in case you need to await for a different method the solution would be similar,
simply toggle the boolean _udpHandlerCompleted (in my case) in the other function.
Edit:
If my soultion helped you, kindly mark it as the accepted answer. Good luck!

At the end checkChange add Future.delayed(const Duration(seconds: 5), checkChange) then call it once instead of running the timer. You can add/check a boolean flag if you need to kill it at any point.

Related

How to trigger lazy loading function before user reaches scrollController.position.maxScrollExtent?

The result I want to achieve, is to trigger a lazy loading function before user reaches scrollController.position.maxScrollExtent, so in that way I will "minimize" the waiting time in the eyes of user.
In my case, I want the lazy loading function to trigger every time users scroll an 80% of the screen
Here is my working code snippet with lazy loading function triggered when user reaches the end of the screen (classic way):
scrollController.addListener(() {
if (scrollController.position.pixels == scrollController.position.maxScrollExtent) {
print('bottomReached');
// lazy load function
}
});
I tried to change the if statement to this:
scrollController.position.pixels >= scrollController.position.maxScrollExtent*0.8
but it didn't work as expected. What else can I do ? Thanks in advance.
You have to set a delta limit, which checks if the scroll is about to read max extent.
scrollController.addListener(() {
if (scrollController.position.pixels > scrollController.position.maxScrollExtent*0.8) {
print('bottomReaching');
// lazy load function
}
});
Here it checks if the current pixel is more than the delta value (maxExtent * 0.8) and if true, loads the function.
One more thing to note is that if in delta range, the function will be triggered with change of every pixel. So it is suggested to have a bool variable outside of scrollController.addListener to check if the function is already running.
For reference https://www.youtube.com/watch?v=EXJboDdIpEA
Happy coding!

Flutter timer doesn't work when changed from milliseconds to seconds

When I change the Duration from milliseconds to seconds the app doesn't load correctly. I get no error in the debug, no logs, nothing, but the app simply doesn't respond to user interaction anymore, it's totally delayed. With milliseconds everything is normal again.
Timer.periodic(Duration(seconds: thisMaxSeconds), (timer) {
code...
}
May I know the value passed in the second (thisMaxSeconds) parameter of the Duration? If it's too high then periodic code execution will be delayed.
The likely cause here is that the thisMaxSeconds variable is set to 0. This causes the loop to run as quickly as possible, blocking UI interaction and updates.
Paste the following into dartpad to play around.
import 'dart:async';
void main() {
Timer.periodic(Duration(seconds: 0), (timer) {
print('Tick: ${timer.tick}');
if(timer.tick == 5){
print('Done');
timer.cancel();
}
});
}

Accessing and cancelling a periodic timer in dart/flutter

I am using a periodic timer for fetching data from server. I need to conditionally stop the timer when a bool (userExpectingTimerBool) has been set to false and also explicitly stop it by user. After stop, neither the periodic timer _timer, nor the last scheduled callback timer _t (or t) should run.
I have a fully working code now. But I feel some of it is redundant and some aspects of periodic timer is still not clear to me after reading documentation. Code is included below.
I have four questions:
Do I need to stop both _timer and _t (or t) to stop all scheduled tasks?
Which timer should I use to display search count (t.tick/_t.tick/_timer.tick)?
The callback function is a transient (nameless) method. Is the timer passed as an argument to it (t) a persistent or transient object (i.e., its value will be preserved across runs)?
What parts of the below code are redundant/can be reduced?
periodic timer with conditional run:
Timer _timer;
Timer _t;
bool userExpectingTimerBool=false;
var timerSearchCount;
var tSearchCount;
_timer = Timer.periodic(
Duration(seconds: intervalGiven),
(Timer t) {
_t= t;
if (userExpectingTimerBool == false) t.cancel();
tSearchCount=t.tick;
_displayResult();
}
);
timerSearchCount=_timer?.tick;
user-controlled stop timer function:
void stoptimer(){
setuserExpectingTimerBool(false);
if (_t !=null){_t!.cancel();}
if (_timer !=null){_timer!.cancel();}
checktimerActive();
}
When Timer.periodic invokes its callback, it should invoke it with itself as the callback's Timer argument. Therefore _t is just an alias for _timer, and having both is pointless. (You can verify this for yourself by printing identityHashCode(timer) and comparing against identityHashCode(_timer).)
See #1. Does not matter.
See #1. There is a single Timer object created by Timer.periodic.
if (userExpectingTimerBool == false)
If userExpectingTimerBool is not nullable, then it would be clearer as if (!userExpectingTimerBool). (If you believe that == false is more readable, then why not if ((userExpectingTimerBool == false) == true) and if (((userExpectingTimerBool == false) == true) == true), etc.) userExpectingTimerBool seems unnecessary anyway; if stoptimer is called, the Timer will be cancelled and prevent additional callbacks. You therefore shouldn't need to perform an additional cancellation check within the callback.
if (_timer !=null){_timer!.cancel();}
As written, _timer is non-nullable, so the null check is unnecessary. If you intend for _timer to be nullable, then you can replace the explicit if check with just _timer?.cancel();.

Why Future block the ui but network requests don't in flutter

I know Future will run in event queue.But event queue are also running on main isolate, if i do some heavy task (for example, calculate sum from 1 to 1000000) in future, it will block my ui code.
But Future in network operation will not block ui (such as await httpClient.getUrl(uri)).
Why does a network request using future take several seconds without blocking the UI, while computational operations block the UI?
#override
void initState() {
super.initState();
Future((){
var result;
for (var i = 0; i < 1000000; ++i) {
result = 'result is $i';
}
print(result);
});
}
if i do some heavy task using Future in initState(), the ui will be blocked.
Isolates in Dart are single-threaded. An isolate can do only one thing at a time.
Asynchronous functions are basically a form of cooperative multitasking. A function must yield (usually via await) to allow other operations to execute in the isolate.
Your computation doesn't yield, so it must run in its entirety before the UI can resume processing events, and the UI will be unresponsive. If you altered it:
Future(() async {
var result;
for (var i = 0; i < 1000000; ++i) {
result = 'result is $i';
await Future.delayed(Duration.zero);
}
print(result);
});
then you should find that the UI can process events regularly and should have the appearance of remaining responsive. (Note that your computation will take much longer to complete because of the additional extra overhead.)
Let me answer briefly, the network request (HttpClient in dart:io) actually ended up in another isolate.
find _NativeSocket section inside socket_patch.dart file, keep searching down and you will see this statement (the link is likely to point to the wrong line as the SDK is constantly updated in the future):
_EventHandler._sendData(this, eventPort!.sendPort, fullData);
Does it look familiar?

Trying to program a timer to do something when it reaches 5 seconds

I can't seem to figure out what to do here. I'm creating a learn to type game where if the user doesn't get the word correct in 5 seconds, they lose a life.
Either this could be implemented by counting down to 0 from 5, or counting up to 5.
I tried using the stopwatch for c# system diagnostics, which works for everything except I can't figure out how to check when it hits 5 seconds. It is a time object you cant compare it to an int.
you have two options here.
You can check the time in the Update function with Time.time or Time.deltaTime, or you can use a Coroutine with the new WaitForSeconds(5) object.
It could be as simple as:
float time;
void Update()
{
time += Time.deltaTime;
if(time > 5)
{
LoseALife();
}
}
If you need to restart the timer each time you complete a word you might have an aditional method like:
public void RestartTimer()
{
time = 0;
}
This would make the timer go back to 0 any moment you need it.