How to make a delayed future cancelable in Dart?

750

Solution 1

You cannot cancel an existing Future. If you do:

Future.delayed(
  Duration(seconds: 2),
  () {
    print('hello');
  },
);

as long as the process runs (and is processing its event queue) for at least 2 seconds, the Future eventually will execute and print 'hello'.

At best you can cause one of the Future's completion callbacks to fire prematurely so that callers can treat the operation as cancelled or failed, which is what CancelableOperation, et al. do.

Edit:

Based on your updated question, which now asks specifically about delayed Futures, you instead should consider using a Timer, which is cancelable. (However, unlike a Future, callers cannot directly wait on a Timer. If that matters to you, you would need to create a Completer, have callers wait on the Completer's Future, and let the Timer's callback complete it.)

Solution 2

Use Timer:

var t = Timer(Duration(seconds: 400), () async {
   client.close(force: true);
});
...
t.cancel();

Solution 3

Using CancalableOperation will not stop print('hello'); from executing even if you cancel. What it does is canceling(discarding) the result(void in your case). I will give you 2 examples using CancalableOperation and CancalableFuture.

CancelableOperation example

    final delayedFuture = Future.delayed(
      Duration(seconds: 2),
      () {
        return 'hello';
      },
    );

    final cancellableOperation = CancelableOperation.fromFuture(
      delayedFuture,
      onCancel: () => {print('onCancel')},
    );

    cancellableOperation.value.then((value) => {
          // Handle the future completion here
          print('then: $value'),
        });
    cancellableOperation.value.whenComplete(() => {
          print('onDone'),
        });
    cancellableOperation.cancel(); // <- commment this if you want to complete

CancelableFuture example

    final delayedFuture = ...;

    final cancalableFuture = CancelableFuture<String>(
      future: delayedFuture,
      onComplete: (result) {
        // Use the result from the future to do stuff
        print(result);
      },
    );
    cancalableFuture.cancel(); // <- commment this if you want to complete

And the CancelableFuture implementation

class CancelableFuture<T> {
  bool _cancelled = false;
  CancelableFuture({
    @required Future<dynamic> future,
    @required void Function(T) onComplete,
  }) {
    future.then((value) {
      if (!_cancelled) onComplete(value);
    });
  }
  void cancel() {
    _cancelled = true;
  }
}
Share:
750
Nerdy Bunz
Author by

Nerdy Bunz

Hi. Coding and creating new things gets me excited like a fizzy drink, but sometimes I run into headaches, so I come here for help. But I make sure to help other people too, so I think it works out. Apparently my posts often contain language considered extraneous by the pool cleaning robots who dutifully scrub it away on their regular patrols. "Bzzzz! Bzzzz!!! Desaturate, desaturate!" Fortunately, it's ACCESS DENIED for any editing attempts on this lil' article. They'll have to re-route the encryptions... and good luck with that. Oooo! Butterfly!!!! Anyway... when I'm not on here, I like to spend time sniffing and paddling my way around the enchanted river-forest near my house... and knitting my very own apps.

Updated on December 28, 2022

Comments

  • Nerdy Bunz
    Nerdy Bunz 10 months

    Lets say that in Dart/Flutter you have the following code:

    void myOperation() {
      // could be anything
      print('you didn't cancel me!');
    }
    

    Notice that the operation itself is not asynchronous and is void -- does not return anything.

    We want it to execute at some point in the future, but we also want to be able to cancel it (because a new operation has been requested that supersedes it).

    I've started by doing this:

    Future.delayed(Duration(seconds: 2), myOperation())
    

    ... but this is not cancellable.

    How exactly could you schedule that "operation," but also make it cancelable?

    I'm thinking... we could modify the code like so:

    Future.delayed(Duration(seconds: 2), () {
        if (youStillWantThisToExecute) {
            print('you didn't cancel me!');
        }
    });
    

    But that's not really very good because it depends on a "global" boolean... and so if the boolean gets flipped to false, no operations will complete, even the most recently requested, which is the one we want to complete.

    It would be nicer if there were a way to create any number of instances of the operation and cancel them on an individual basis... or to have a unique id assigned to each operation, and then instead of having a boolean control whether or not to execute... to have a "mostRecentId" int or something which is checked prior to execution.

    Anyways...

    CancelableOperation seemed promising just from its name.

    So, I looked at its documentation:

    CancelableOperation.fromFuture(Future inner, {FutureOr onCancel()}) Creates a CancelableOperation wrapping inner. [...] factory

    But honestly that just makes my poor head hurt oh so much.

    I've consulted other articles, questions, and answers, but they are all part of some specific (and complex) context and there isn't a dirt simple example anywhere to be found.

    Is there a way to make a delayed future cancellable by wrapping it in some other class?

    Can someone more experienced please provide at least one simple, complete, verified example that compiles in DartPad?

    Thanks.

  • Nerdy Bunz
    Nerdy Bunz over 2 years
    So I see how to cancel the CancelableFuture using .cancel(), but how do to start/run it in the first place? cancelableFuture.run()? no.cancelableFuture()? no. :S
  • YoBo
    YoBo over 2 years
    You run the future here: Future.delayed(. You are just passing the Future and if you cancel, the onComplete will not fire. Thats it. Maybe I did not get the question right. What exactly are you trying to do with a Future that requires canceling? Can you give more info so we can try to come up with a solution.
  • Nerdy Bunz
    Nerdy Bunz over 2 years
    No you got it right I'm sure. The question isn't that great. I'll edit it a little bit. I'm confused mainly because I'm just trying to put it all together in dartpad so I understand it, but I'm getting errors that I don't understand.