Search code examples
flutterdartasynchronous

Is it possible to add some delay to an async function without making the total delay more than the time taken by the function itself?


I have a function that makes an API call and usually the response is received after a delay of anywhere between 50 milliseconds to 6 seconds. I want the result to arrive exactly at 1500 milliseconds if the API call completed within 1500 milliseconds, and if the API response completed after 1500 milliseconds, it should return the result as soon as the response is received.

I tried using this:

final result = await Future.delayed(Duration(milliseconds: 1500,() async => await apiCall();

But the problem with this is that the apiCall() function is getting called only after the delay of 1500 mils.

Is there a direct way in dart to achieve this? Have anyone tried this before?


Solution

  • It's fairly straightforward to make a helper function that measures the amount of time taken by the API call and then adds an appropriate delay before returning the result:

    Future<R> callWithMinimumDuration<R>(
      Future<R> Function() func,
      Duration minimumDuration,
    ) async {
      var stopwatch = Stopwatch()..start();
      var result = await func();
      var delta = minimumDuration - stopwatch.elapsed;
      if (delta > Duration.zero) {
        await Future.delayed(delta);
      }
      return result;
    }
    

    And then you'd invoke it with:

    var result = await callWithMinimumDuration(apiCall, Duration(milliseconds: 1500));
    

    Alternatively, you could use Future.wait to combine the original Future with a Future.delayed. However, Future.wait wants Futures with homegeneous types, so you'd either need casts (whether explicit or implicit, if implicit dynamic casts are allowed):

    Future<R> callWithMinimumDuration<R>(
      Future<R> Function() func,
      Duration minimumDuration,
    ) async =>
        (await Future.wait<Object?>([func(), Future.delayed(minimumDuration)]))[0]
            as R;
    

    or use an assignment technique to deal with heterogeneous Futures:

    Future<R> callWithMinimumDuration<R>(
      Future<R> Function() func,
      Duration minimumDuration,
    ) async {
      late R result;
      await Future.wait([
        () async {
          result = await func();
        }(),
        Future.delayed(minimumDuration),
      ]);
      return result;
    }
    
    

    You also could use one of the .wait extensions, but proper error-handling would be a bit more work:

    Future<R> callWithMinimumDuration<R>(
      Future<R> Function() func,
      Duration minimumDuration,
    ) async {
      try {
        return (await (func(), Future.delayed(minimumDuration)).wait).$1;
      } on ParallelWaitError catch (e, st) {
        Error.throwWithStackTrace(e.errors.$1, st);
      }
    }