getAndPut method

  1. @override
Future<T?> getAndPut(
  1. String key,
  2. T value, {
  3. CacheEntryDelegate<T>? delegate,
})
override

Associates the specified value with the specified key in this cache, returning an existing value if one existed. If the cache previously contained a mapping for the key, the old value is replaced by the specified value.

  • key: key with which the specified value is to be associated
  • value: value to be associated with the specified key
  • delegate: provides the caller a way of changing the CacheEntry before persistence

The previous value is returned, or null if there was no value associated with the key previously.

Implementation

@override
Future<T?> getAndPut(String key, T value, {CacheEntryDelegate<T>? delegate}) {
  // Current time
  final now = clock.now();
  // #region Statistics
  Stopwatch? watch;
  Future<CacheEntry?> Function(CacheEntry? entry) posGet =
      (CacheEntry? entry) => Future.value(entry);
  Future<T?> Function(bool ok, T? value) posPut =
      (bool ok, T? value) => Future<T?>.value(value);
  if (statsEnabled) {
    watch = clock.stopwatch()..start();
    posGet = (CacheEntry? entry) {
      if (entry == null || entry.isExpired(now)) {
        stats.increaseMisses();
      } else {
        stats.increaseGets();
      }
      if (watch != null) {
        stats.addGetTime(watch.elapsedMicroseconds);
      }

      return Future.value(entry);
    };
    posPut = (bool ok, T? value) {
      if (ok) {
        stats.increasePuts();
      }
      if (watch != null) {
        stats.addPutTime(watch.elapsedMicroseconds);
        watch.stop();
      }

      return Future<T?>.value();
    };
  }
  // #endregion

  // Try to get the entry from the cache
  return _getStorageEntry(key).then(posGet).then((entry) {
    final expired = entry != null && entry.isExpired(now);

    // If the entry exists on cache but is already expired we remove it first
    final pre = expired
        ? _removeStorageEntry(
            key, CacheEntryExpiredEvent<T>(this, entry.info))
        : Future.value();

    // If the entry is expired or non existent
    if (entry == null || expired) {
      return pre.then((_) =>
          _newEntry(_entryBuilder(key, value, now, delegate: delegate))
              .then((bool ok) => posPut(ok, null)));
    } else {
      return pre
          .then((_) => _replaceEntry(entry.info, value, now))
          .then((_) => posPut(true, entry.value));
    }
  });
}