Skip to content

Instantly share code, notes, and snippets.

@marvin-kolja
Last active October 4, 2023 08:43
Show Gist options
  • Save marvin-kolja/6181915ee0bc0992db83c06aa23f350c to your computer and use it in GitHub Desktop.
Save marvin-kolja/6181915ee0bc0992db83c06aa23f350c to your computer and use it in GitHub Desktop.
Custom Dart Periodic - with duration multiplier, run limit, and immediate callback execution
import 'dart:async';
/// This class allows to create a periodic timer that can run immediately
/// ([_runImmediately]) and can change its duration over time
/// ([_durationMultiplier]).
///
/// The [_maxRuns] is the maximum number of times the timer will run. If the
/// timer is cancelled, it will stop running before reaching the maximum number
/// of runs. If the maximum number of runs is 0, the timer will run
/// indefinitely.
///
/// The [_durationMultiplier] is the factor by which the duration of the timer
/// will be multiplied after each run. For example, if the duration is 1 second
/// and the multiplier is 2, the duration of the next run will be 2 seconds,
/// then 4, then 8, and so on.
///
/// The [_maxDuration] is the maximum duration of the timer. If the duration
/// exceeds the maximum duration, the timer will stop running. If the maximum
/// duration is 0, the timer will run indefinitely (which is the default).
///
/// The [_runImmediately] is a boolean that determines whether the timer will
/// run immediately after being created or not.
///
class Periodic {
Timer? _timer;
bool _cancelled = false;
final bool _runImmediately;
final double _durationMultiplier;
final Duration _maxDuration;
final int _maxRuns;
void cancel() {
_cancelled = true;
_timer?.cancel();
}
Periodic._internal({
required bool runImmediately,
required double durationMultiplier,
required int maxRuns,
required Duration maxDuration,
}) : _runImmediately = runImmediately,
_durationMultiplier = durationMultiplier,
_maxDuration = maxDuration,
_maxRuns = maxRuns;
factory Periodic.run(
Duration duration,
Future<void> Function(Periodic) callback, {
bool runImmediately = false,
double durationMultiplier = 1.0,
Duration maxDuration = Duration.zero,
int maxRuns = 0,
}) {
if (maxRuns < 0) {
throw ArgumentError.value(
maxRuns,
'maxRuns',
'Must be greater than or equal to 0 for infinite runs.',
);
}
return Periodic._internal(
runImmediately: runImmediately,
durationMultiplier: durationMultiplier,
maxDuration: maxDuration,
maxRuns: maxRuns,
).._run(duration, callback);
}
void _run(
Duration duration,
Future<void> Function(Periodic) callback, {
int runCount = 1,
}) {
if (_cancelled) return;
if (_maxRuns > 0 && runCount > _maxRuns) return;
if (_maxDuration > Duration.zero && duration > _maxDuration) return;
if (_runImmediately && runCount == 1) callback(this);
_timer = Timer(duration, () async {
await callback(this);
_run(duration * _durationMultiplier, callback, runCount: runCount + 1);
});
}
}
@marvin-kolja
Copy link
Author

Replaced old periodic with the revised version. It provides more flexibility and control over the periodic task's execution but keeps the interface clean. For instance, instead of passing maxRuns, you can now check the run count in the callback and cancel as you see fit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment