Created
November 27, 2022 04:41
-
-
Save matanlurey/63773b3c8ef978d46bce302f54970e9e to your computer and use it in GitHub Desktop.
Example of a short-lived value in Dart.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// 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