Flutter/Dart:你能 运行 后台服务 Future.delayed 吗?
Flutter/Dart: can you run a background service with Future.delayed?
我每天必须在 Dart/Flutter 项目上 运行 一堆任务。我目前是这样做的:
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());
}
}
所以我一直在想:这是不是错了?如果可行,我为什么要使用像 https://pub.dev/packages/cron 这样的包来执行此操作?
回顾您的示例,您最初使用 Timer(),然后 _runWorkersLoop() 通过使用 Future.delayed() 调用自身来实现它自己的“周期性”循环。可能简化此操作的一种方法是使用 Timer.periodic() 您调用一次,直到您取消它,它会重复。
https://api.dart.dev/stable/2.12.0/dart-async/Timer/Timer.periodic.html
然后你有一个计时器实例,你可以检查它是否 运行 isRunning() 并且你可以随时使用 cancel() 取消。
我查看了 cron lib 的源代码,它使用类似于 Future.delayed 的 Future.microtask。通常使用这样的库可以帮助您:
- 与自行开发的解决方案相比,更多关注修复任何错误的代码
- 您以后可能想要使用的更多通用功能
- 对于通过更多可用示例获取您的代码的人来说learn/understand更容易
我假设您在运行时没有任何精确到毫秒的关键计时要求,所以我认为您可能有兴趣查看上面提到的周期性计时器。
你可能想要保护的一件事是,如果错误稍后调用你的 _runWorkersLoop() 函数,而它已经 运行,它会再次调用 Future.delayed(),即使一个已经在等待.您没有办法检查现有的 Future.delayed() 实例,但是使用 Timer.peridic() 您可以使用“isRunning()”进行检查。
这是@Eradicatore评论后的改进版本,以防有人感兴趣。按承诺工作。
class TaskScheduler {
Timer _timer;
/// Launch the daily tasks.
/// If [forceUpdate] is true, any currently running task will be canceled and rerun.
/// Otherwise a new task will only be started if no previous job is running.
void launchDailyTasks({bool forceUpdate = false}) async {
bool checkLastSync = true;
if (forceUpdate) {
//cancel
if (_timer != null) _timer.cancel();
checkLastSync = false;
} else {
//don't start tasks if a previous job is running
if (_timer != null && _timer.isActive) {
return;
}
}
Duration startDelay = Duration();
if (checkLastSync) {
//check last sync date to determine when to start the timer
SharedPreferences prefs = await SharedPreferences.getInstance();
int timestamp = prefs.getInt(prefsKey(AppConstants.LAST_SYNC));
if (timestamp == null) timestamp = DateTime.now().millisecondsSinceEpoch;
final difference = DateTime.now()
.difference(DateTime.fromMillisecondsSinceEpoch(timestamp));
if (difference.inDays < 1) {
//start the timer when 1 day is reached
startDelay = Duration(
seconds: Duration(days: 1).inSeconds - difference.inSeconds);
}
}
//start tasks
Future.delayed(startDelay, () {
//run first tasks immediately once
_runWorkersLoop();
//setup periodic after
_timer = Timer.periodic(Duration(days: 1), (Timer t) {
_runWorkersLoop();
});
});
}
void _runWorkersLoop() async {
_startDailyTasks();
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setInt(prefsKey(AppConstants.LAST_SYNC),
DateTime.now().millisecondsSinceEpoch);
}
}
我每天必须在 Dart/Flutter 项目上 运行 一堆任务。我目前是这样做的:
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());
}
}
所以我一直在想:这是不是错了?如果可行,我为什么要使用像 https://pub.dev/packages/cron 这样的包来执行此操作?
回顾您的示例,您最初使用 Timer(),然后 _runWorkersLoop() 通过使用 Future.delayed() 调用自身来实现它自己的“周期性”循环。可能简化此操作的一种方法是使用 Timer.periodic() 您调用一次,直到您取消它,它会重复。
https://api.dart.dev/stable/2.12.0/dart-async/Timer/Timer.periodic.html
然后你有一个计时器实例,你可以检查它是否 运行 isRunning() 并且你可以随时使用 cancel() 取消。
我查看了 cron lib 的源代码,它使用类似于 Future.delayed 的 Future.microtask。通常使用这样的库可以帮助您:
- 与自行开发的解决方案相比,更多关注修复任何错误的代码
- 您以后可能想要使用的更多通用功能
- 对于通过更多可用示例获取您的代码的人来说learn/understand更容易
我假设您在运行时没有任何精确到毫秒的关键计时要求,所以我认为您可能有兴趣查看上面提到的周期性计时器。
你可能想要保护的一件事是,如果错误稍后调用你的 _runWorkersLoop() 函数,而它已经 运行,它会再次调用 Future.delayed(),即使一个已经在等待.您没有办法检查现有的 Future.delayed() 实例,但是使用 Timer.peridic() 您可以使用“isRunning()”进行检查。
这是@Eradicatore评论后的改进版本,以防有人感兴趣。按承诺工作。
class TaskScheduler {
Timer _timer;
/// Launch the daily tasks.
/// If [forceUpdate] is true, any currently running task will be canceled and rerun.
/// Otherwise a new task will only be started if no previous job is running.
void launchDailyTasks({bool forceUpdate = false}) async {
bool checkLastSync = true;
if (forceUpdate) {
//cancel
if (_timer != null) _timer.cancel();
checkLastSync = false;
} else {
//don't start tasks if a previous job is running
if (_timer != null && _timer.isActive) {
return;
}
}
Duration startDelay = Duration();
if (checkLastSync) {
//check last sync date to determine when to start the timer
SharedPreferences prefs = await SharedPreferences.getInstance();
int timestamp = prefs.getInt(prefsKey(AppConstants.LAST_SYNC));
if (timestamp == null) timestamp = DateTime.now().millisecondsSinceEpoch;
final difference = DateTime.now()
.difference(DateTime.fromMillisecondsSinceEpoch(timestamp));
if (difference.inDays < 1) {
//start the timer when 1 day is reached
startDelay = Duration(
seconds: Duration(days: 1).inSeconds - difference.inSeconds);
}
}
//start tasks
Future.delayed(startDelay, () {
//run first tasks immediately once
_runWorkersLoop();
//setup periodic after
_timer = Timer.periodic(Duration(days: 1), (Timer t) {
_runWorkersLoop();
});
});
}
void _runWorkersLoop() async {
_startDailyTasks();
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setInt(prefsKey(AppConstants.LAST_SYNC),
DateTime.now().millisecondsSinceEpoch);
}
}