I have to run a bunch of tasks every day on a Dart/Flutter project. This is how I currently do it:
class TaskScheduler {
DateTime _lastUpdate;
bool _isRunning = false;
void launchDailyTasks() async {
//make sure tasks are not already scheduled
if (_isRunning) return;
//check last updates
if (_lastUpdate == null) {
SharedPreferences prefs = await SharedPreferences.getInstance();
final _stamp = prefs.getInt(prefsKey(AppConstants.LAST_SYNC));
if (_stamp != null) {
_lastUpdate = DateTime.fromMillisecondsSinceEpoch(_stamp);
} else {
_lastUpdate = DateTime.now();
}
}
if (_lastUpdate.isBefore(DateTime.now().add(Duration(days: 1)))) {
_runWorkersLoop();
} else {
final _delay =
DateTime.now().difference(_lastUpdate.add(Duration(days: 1)));
Timer(_delay, () => _runWorkersLoop());
}
}
void _runWorkersLoop() async {
_isRunning = true;
_startDailyTasks();
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setInt(prefsKey(AppConstants.LAST_SYNC),
DateTime.now().millisecondsSinceEpoch);
_lastUpdate = DateTime.now();
Future.delayed(Duration(days: 1), () => _runWorkersLoop());
}
}
And so I've been wondering: is this wrong? Why should I use a package like https://pub.dev/packages/cron to do this if this works?
In reviewing your example, you use Timer() initially, and then _runWorkersLoop() implements it's own "periodic" loop by calling itself with Future.delayed(). One way to maybe simplify this is to use Timer.periodic() which you call once and until you cancel it, it will repeat.
https://api.dart.dev/stable/2.12.0/dart-async/Timer/Timer.periodic.html
Then you have a timer instance that you can check if it's running with isRunning() and you can cancel at any time with cancel().
I looked at the source for cron lib and it uses Future.microtask which is similar to Future.delayed. Generally using a lib like that will help give you:
I assume you don't have any critical timing requirements down to the millisecond with when your stuff runs, so I think you might be interested in looking at a periodic timer as mentioned above.
One thing you might want to protect is that if a bug later calls your _runWorkersLoop() function when it's already running, it will call Future.delayed() again even though one already is waiting. You don't have a way to check for existing Future.delayed() instances but with Timer.peridic() you can use the "isRunning()" to check.