Skip to content

Instantly share code, notes, and snippets.

@matanlurey
Created November 27, 2022 04:41
Show Gist options
  • Save matanlurey/63773b3c8ef978d46bce302f54970e9e to your computer and use it in GitHub Desktop.
Save matanlurey/63773b3c8ef978d46bce302f54970e9e to your computer and use it in GitHub Desktop.
Example of a short-lived value in Dart.
/// A short-lived [value] `T`, that conditionally is re-computed or re-fetched.
class Ephemeral<T> {
final Future<T> Function() _fetch;
final bool Function(T) _isExpired;
/// Returns [value] by invoking [fetch].
///
/// If [isExpired] returns `false` for a given value, the value is re-fetched.
///
/// ```
/// // A silly example that refetches a name when it matches the "stale" name.
///
/// Future<String> readName() { /* ... */ }
/// String getStaleName() { /* ... */ }
///
/// final value = Ephemeral.fetchWhen(readName, (name) {
/// return name == getStaleName();
/// });
/// ```
factory Ephemeral.fetchWhen(
Future<T> Function() fetch,
bool Function(T) isExpired,
) = Ephemeral._;
/// Returns [value] by invoking [fetch], re-fetching after [duration].
///
/// ```
/// Ephemeral.cacheFor(fetchNames, Duration(seconds: 60));
/// ```
factory Ephemeral.cacheFor(
Future<T> Function() fetch,
Duration duration, {
DateTime Function() now = DateTime.now,
}) {
late DateTime lastFetch;
Future<T> fetchAndStore() {
return fetch().then((value) {
lastFetch = now();
return value;
});
}
return Ephemeral.fetchWhen(fetchAndStore, (_) {
final delta = now().difference(lastFetch);
return delta >= duration;
});
}
Ephemeral._(this._fetch, this._isExpired);
/// Underlying value.
///
/// This getter is _not_ guaranteed to be stable over time, and in fact part
/// of the contract is that [value] is discarded and either re-computed or
/// re-fetched.
Future<T> get value async {
var future = _lastFuture;
if (future != null) {
if (_isExpired(_lastValue)) {
_lastFuture = null;
} else {
return future;
}
}
future = _fetch();
_lastFuture = future;
_lastValue = await _lastFuture as T;
return future;
}
// If not-null, this is the last fetched future.
Future<T>? _lastFuture;
// If _lastFuture is not-null, this is the last unwrapped result.
late T _lastValue;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment