throttleFuture<T> method

Future<T> throttleFuture<T>({
  1. Duration? duration,
  2. List<Object?> positionalArgs = const [],
  3. Map<Symbol, Object?> namedArgs = const {},
})

针对future的节流 推荐在无参数输入的场景下使用

Implementation

Future<T> throttleFuture<T>({
  Duration? duration,
  List<Object?> positionalArgs = const [],
  Map<Symbol, Object?> namedArgs = const {},
}) {
  late final String key;
  if (positionalArgs.isEmpty && namedArgs.isEmpty) {
    key = this.hashCode.toString();
  } else {
    final keyBuffer = StringBuffer();
    keyBuffer.write(this.hashCode);
    for (final arg in positionalArgs) {
      keyBuffer.write('_');
      keyBuffer.write(arg.hashCode);
    }
    for (final entry in namedArgs.entries) {
      keyBuffer.write('_');
      keyBuffer.write(entry.key.hashCode);
      keyBuffer.write('_');
      keyBuffer.write(entry.value.hashCode);
    }
    key = keyBuffer.toString();
  }
  final now = DateTime.now();
  final cached = ThrottleUtil.instance.throttleCache[key];

  if (cached != null) {
    if (cached.pendingFuture != null) {
      return cached.pendingFuture as Future<T>;
    }
    if (duration != null && cached.completedFuture != null) {
      if (now.difference(cached.lastCall) < duration) {
        return cached.completedFuture as Future<T>;
      }
    }
  }

  final completer = Completer<T>();
  final future = Function.apply(this, positionalArgs, namedArgs) as Future<T>;
  future
      .then((value) {
        if (!completer.isCompleted) completer.complete(value);
        ThrottleUtil.instance.throttleCache[key] = _ThrottleData<T>(
          lastCall: now,
          completedFuture: Future.value(value),
          pendingFuture: null,
          pendingCompleter: null,
        );
        return value;
      })
      .catchError((error) {
        if (!completer.isCompleted) completer.completeError(error);
        ThrottleUtil.instance.throttleCache.remove(key);
        throw error;
      });

  ThrottleUtil.instance.throttleCache[key] = _ThrottleData<T>(
    lastCall: now,
    completedFuture: null,
    pendingFuture: future,
    pendingCompleter: completer,
  );

  return completer.future;
}