Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elegant way to cancel future on timeout

Tags:

flutter

dart

I have these scenarios where I need to attempt something for some time or so many times, but not for too long. The .timeout property of the Future looked like a natural choice. What I learned is that the original Future keeps on going even after it is timed out e.g.:

  Future<void> trySomething() async {
    while (true) {
      print('trying something...');
      bool successful = false; // exit if done
      if (successful) {
        return;
      }
      await Future.delayed(const Duration(seconds: 1));
    }
  }

  trySomething().timeout(const Duration(seconds: 10), onTimeout: () {
    print('try something else...');
  });


above code produces following output:

I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): try something else...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...

My not-so-elegant work around was to use external flag variable to indicate to the process inside the main future that its services are no longer required:

  bool youAreTooLate = false;
  Future<void> trySomething() async {
    while (true) {
      if (youAreTooLate) {
        return;
      }
      print('trying something...');
      bool successful = false; // exit if done
      if (successful) {
        return;
      }
      await Future.delayed(const Duration(seconds: 1));
    }
  }


  trySomething().timeout(const Duration(seconds: 10), onTimeout: () {
    youAreTooLate = true;
    print('try something else...');
  });

This produces the expected result below, but doesn't make me all that warm and fuzzy inside.

I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): trying something...
I/flutter (26426): try something else...

Is there a better way to handle this situation?

like image 872
Denis G Avatar asked Jan 17 '26 11:01

Denis G


1 Answers

If you're just trying to do something repeatedly with a delay, you can use a Timer to achieve this while maintaining the ability to cancel it.

final timer = Timer.periodic(const Duration(seconds: 1), (t) {
  // Do repeated work here.

  if (shouldCancel) {
    // This is the 'timer' object.
    t.cancel();
  }
});

Future.delayed(const Duration(seconds: 10)).then((_) => timer.cancel());

If you're looking to actually wait for the work inside the timer to complete before retrying, you can do something like this:

Timer timer;

doWork() async {
  // Do your work here.
  // ...

  if (retry) {
    timer = Timer(const Duration(seconds: 1), doWork);
  }
}

Future.delayed(const Duration(seconds: 10)).then((_) => timer?.cancel());

This should work, but if you already have an uncompleted Future there's no way to actually cancel it.

like image 200
Ben Konyi Avatar answered Jan 20 '26 05:01

Ben Konyi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!