is there any way to cancel a dart Future?

后端 未结 9 1483
礼貌的吻别
礼貌的吻别 2020-11-27 05:55

In a Dart UI, I have a button [submit] to launch a long async request. The [submit] handler returns a Future. Next, the button [submit] is replaced by a button [cancel] to a

9条回答
  •  渐次进展
    2020-11-27 06:10

    You can use CancelableOperation or CancelableCompleter to cancel a future. See below the 2 versions:

    Solution 1: CancelableOperation (included in a test so you can try it yourself):

    • cancel a future

    test("CancelableOperation with future", () async {
    
      var cancellableOperation = CancelableOperation.fromFuture(
        Future.value('future result'),
        onCancel: () => {debugPrint('onCancel')},
      );
    
    // cancellableOperation.cancel();  // uncomment this to test cancellation
    
      cancellableOperation.value.then((value) => {
        debugPrint('then: $value'),
      });
      cancellableOperation.value.whenComplete(() => {
        debugPrint('onDone'),
      });
    });
    
    • cancel a stream

    test("CancelableOperation with stream", () async {
    
      var cancellableOperation = CancelableOperation.fromFuture(
        Future.value('future result'),
        onCancel: () => {debugPrint('onCancel')},
      );
    
      //  cancellableOperation.cancel();  // uncomment this to test cancellation
    
      cancellableOperation.asStream().listen(
            (value) => { debugPrint('value: $value') },
        onDone: () => { debugPrint('onDone') },
      );
    });
    

    Both above tests will output:

    then: future result
    onDone
    

    Now if we uncomment the cancellableOperation.cancel(); then both above tests will output:

    onCancel
    

    Solution 2: CancelableCompleter (if you need more control)

    test("CancelableCompleter is cancelled", () async {
    
      CancelableCompleter completer = CancelableCompleter(onCancel: () {
        print('onCancel');
      });
    
      // completer.operation.cancel();  // uncomment this to test cancellation
    
      completer.complete(Future.value('future result'));
      print('isCanceled: ${completer.isCanceled}');
      print('isCompleted: ${completer.isCompleted}');
      completer.operation.value.then((value) => {
        print('then: $value'),
      });
      completer.operation.value.whenComplete(() => {
        print('onDone'),
      });
    });
    

    Output:

    isCanceled: false
    isCompleted: true
    then: future result
    onDone
    

    Now if we uncomment the cancellableOperation.cancel(); we get output:

    onCancel
    isCanceled: true
    isCompleted: true
    

    Be aware that if you use await cancellableOperation.value or await completer.operation then the future will never return a result and it will await indefinitely if the operation was cancelled. This is because await cancellableOperation.value is the same as writing cancellableOperation.value.then(...) but then() will never be called if the operation was cancelled.

    Remember to add async Dart package.

    Code gist

提交回复
热议问题