Ad

Flutter/Dart: Can You Run A Background Service With Future.delayed?

- 1 answer

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?

Ad

Answer

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:

  • more eyes on the code to fix any bugs versus a home grown solution
  • more general functionality that you might want to use later
  • easier to learn/understand for someone picking up your code through more examples of use available

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.

Ad
source: stackoverflow.com
Ad