How to periodically call a function only if it has finished executing the previous time in Dart?
Solution 1
This code should work correctly:
import 'dart:async';
import 'dart:math';
Future<void> main() async {
Timer(Duration(seconds: 0), _callback);
}
Future<void> _callback() async {
print('started: ${DateTime.now()}');
try {
final ms = Random().nextInt(5000);
await Future.delayed(Duration(milliseconds: ms));
} finally {
print('finished: ${DateTime.now()}');
Timer(Duration(seconds: 5), _callback);
}
}
Result:
started: 2020-01-04 17:51:29.401246 finished: 2020-01-04 17:51:32.972114 started: 2020-01-04 17:51:37.973218 finished: 2020-01-04 17:51:38.551234 started: 2020-01-04 17:51:43.552218 finished: 2020-01-04 17:51:47.579374 started: 2020-01-04 17:51:52.580253 finished: 2020-01-04 17:51:55.547275 started: 2020-01-04 17:52:00.548258 finished: 2020-01-04 17:52:01.156281
Solution 2
You can use package https://pub.dev/packages/neat_periodic_task
It has the following features
- Run a periodic task on a loop.
- Limit maximum run-time to prevent a hanging loop.
- Retry failed task executions after a timeout.
- Catch and log all exceptions/errors from the task.
- (Optionally) prevent concurrent tasks using status in a storage service that supports optimistic concurrency (eg. Cloud Datastore, Google Cloud Storage).
- Print log messages liveness monitoring of the periodic tasks.
example code snippet
import 'dart:io';
import 'package:neat_periodic_task/neat_periodic_task.dart';
void main() async {
final scheduler = NeatPeriodicTaskScheduler(
interval: Duration(seconds: 5),
name: 'hello-world',
timeout: Duration(seconds: 5),
task: () async => print('Hello World'),
minCycle: Duration(seconds: 5),
);
scheduler.start();
await ProcessSignal.sigterm.watch().first;
await scheduler.stop();
}
Solution 3
I don't know exactly what return type the DB call has (if any, I'm assuming List<int>
here), but for 5 seconds or (execution time + 5 seconds) you could use something like this simulation:
import 'dart:math';
// simulating network call
Future<List<int>> _fetchMarkersFromDb() async {
await Future.delayed(Duration(seconds: Random().nextInt(7)));
return [Random().nextInt(993) + 27, Random().nextInt(28) + 4];
}
Stream<List<int>> _timer() async* {
while (true) {
yield await _fetchMarkersFromDb();
await Future.delayed(Duration(seconds: 5));
}
}
void main() {
_timer().listen(print);
}
and you could also add throttle/debounce from RxDart
Comments
-
Urmil Shroff over 1 year
I'm making a Flutter app where I need to periodically call a funtion every 5 seconds. Here is my current implementation:
Timer.periodic(markerRefreshInterval, (Timer t) { print('$markerRefreshInterval seconds over, refreshing...'); _fetchMarkersFromDb(); // updates markers every 5 seconds });
However, sometimes (due to network issues, etc.) the
_fetchMarkersFromDb()
function takes longer than 5 seconds to execute. In such cases, I've figured out (with the help of debug prints in the console) that there are multiple instances (threads?) of the same function being executed parallely. This messes up a variable I'm trying to increment in each function call.I want
_fetchMarkersFromDb()
to be called at a minimum interval of 5 seconds, or the total function execution time + 5 seconds (if the function itself takes long to execute). I do not want it to run parallely like it does now. I've heard that Streams can be used, but I'm confused as to how to use them while calling the function repeatedly in an interval.Full source code can be found here, and the issue I'm trying to solve is here. Thanks in advance.