withDelay method

Future<T> withDelay(
  1. Duration minOperationTime, {
  2. Duration threshold = const Duration(milliseconds: 50),
  3. @Deprecated("Will be removed in version 2.0.0, use " "the 'threshold' parameter instead.") int thresholdInMillis = 50,
})

If the time it takes for this to complete is less than minOperationTime, then a Future.delayed is awaited for the remaining time. The delay also affects any errors of this Future.

The Future returned by this method will take at least as much time as specified by minOperationTime to complete.

Implementation

Future<T> withDelay(
  Duration minOperationTime, {
  Duration threshold = const Duration(milliseconds: 50),
  @Deprecated(
    "Will be removed in version 2.0.0, use "
    "the 'threshold' parameter instead.",
  )
  int thresholdInMillis = 50,
}) async {
  // TODO(obemu): remove/change the asserts and if condition in v2.0.0

  // minOperationTime is less than 1 millisecond, therefore
  // we do not add a fake delay.
  if (minOperationTime.inMilliseconds < 1) return this;

  assert(minOperationTime < const Duration(minutes: 10));
  assert(minOperationTime.inMilliseconds >= thresholdInMillis);

  // Can be removed in v2.0.0, once [thresholdInMillis] is removed.
  if (50 != thresholdInMillis) {
    if (const Duration(milliseconds: 50) != threshold)
      throw ArgumentError.value(
        thresholdInMillis,
        "thresholdInMillis",
        "Cannot provide a value for 'thresholdInMillis' and "
            "'threshold', you should only use the 'threshold' parameter",
      );

    threshold = Duration(milliseconds: thresholdInMillis);
  }

  Result<T> res;

  final watch = Stopwatch();
  watch.start();
  try {
    res = Result.value(await this);
    // Forward error objects of any type.
    // ignore: avoid_catches_without_on_clauses
  } catch (err, st) {
    res = Result.error(err, st);
  } finally {
    watch.stop();
  }

  final delta = minOperationTime - watch.elapsed;

  // Only wait if we have at least threshold.
  if (delta >= threshold) {
    // TODO(obemu): Maybe find a better fix for the following hack.
    // If the operation completed too fast then we wait for
    // [minOperationTime], because waiting for [delta]
    // in this case may not work (depends on the platform and
    // compilation type) so that [withDelay] actually causes a delay of at
    // least [minOperationTime].
    final delay = watch.elapsed < const Duration(milliseconds: 100)
        ? minOperationTime
        : delta;

    await Future.delayed(delay);
  }

  if (res.asError case final ErrorResult err)
    throw Error.throwWithStackTrace(err.error, err.stackTrace);

  return res.asValue!.value;
}