有没有办法取消飞镖未来?

时间:2013-07-09 15:54:00

标签: dart dart-async

在Dart用户界面中,我有一个按钮[submit]来启动长异步请求。 [submit]处理程序返回Future。接下来,按钮[submit]被按钮[cancel]替换,以允许取消整个操作。在[取消]处理程序中,我想取消长操作。如何取消提交处理程序返回的Future?我找不到办法做到这一点。

10 个答案:

答案 0 :(得分:5)

您可以使用CancelableOperationCancelableCompleter取消未来。请参见以下2个版本:

解决方案1:CancelableOperation(包含在测试中,您可以自己尝试):

test("CancelableOperation with future", () async {

  var cancellableOperation = CancelableOperation.fromFuture(
    Future.value('future result'),
    onCancel: () => {debugPrint('onCancel')},
  );

  // cancellableOperation.cancel();

  cancellableOperation.value.then((value) => {
        debugPrint('then: $value'),
      });
  cancellableOperation.value.whenComplete(() => {
        debugPrint('onDone'),
      });
});

test("CancelableOperation with stream", () async {

  var cancellableOperation = CancelableOperation.fromFuture(
    Future.value('future result'),
    onCancel: () => {debugPrint('onCancel')},
  );

  //  cancellableOperation.cancel();

  cancellableOperation.asStream().listen(
        (value) => { debugPrint('value: $value') },
        onDone: () => { debugPrint('onDone') },
      );
});

以上两种测试都将输出:

then: future result
onDone

现在,如果我们取消注释cancellableOperation.cancel();,则以上两个测试都将输出:

onCancel

解决方案2:CancelableCompleter(如果您需要更多控制权)

test("CancelableCompleter is cancelled", () async {

  CancelableCompleter completer = CancelableCompleter(onCancel: () {
    print('onCancel');
  });

  // completer.operation.cancel();

  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'),
  });
});

输出:

isCanceled: false
isCompleted: true
then: future result
onDone

现在,如果我们取消注释cancellableOperation.cancel();,我们将得到输出:

onCancel
isCanceled: true
isCompleted: true

请注意,如果您使用await cancellableOperation.valueawait completer.operation,它将永远不会返回结果,并且如果操作被取消,它将无限期地等待。这是因为await cancellableOperation.value与写入cancellableOperation.value.then(...)相同,但是如果操作被取消,则永远不会调用then()

记住要添加async Dart程序包。

Code gist

答案 1 :(得分:3)

据我所知,没有办法取消未来。但有一种方法可以取消Stream订阅,也许这可以帮助你。

在按钮上调用onSubmit会返回StreamSubscription个对象。您可以显式存储该对象,然后在其上调用cancel()以取消流订阅:

StreamSubscription subscription = someDOMElement.onSubmit.listen((data) {

   // you code here

   if (someCondition == true) {
     subscription.cancel();
   }
});

稍后,作为对某些用户操作的回复,您可以取消订阅:

答案 2 :(得分:3)

如何取消Future.delayed

一种简单的方法是改用Timer:)

Timer _timer;

void _schedule() {
  _timer = Timer(Duration(seconds: 2), () { 
    print('Do something after delay');
  });
}

@override
void dispose() {
  super.dispose();
  _timer?.cancel();
}

答案 3 :(得分:2)

我的2美分的价值...

class CancelableFuture {
  bool cancelled = false;
  CancelableFuture(Duration duration, void Function() callback) {
    Future<void>.delayed(duration, () {
      if (!cancelled) {
        callback();
      }
    });
  }

  void cancel() {
    cancelled = true;
  }
}

答案 4 :(得分:1)

我完成“取消”计划执行的一种方法是使用Timer。在这种情况下,我实际上推迟了。 :)

Timer _runJustOnceAtTheEnd;

void runMultipleTimes() {
  if (_runJustOnceAtTheEnd != null) {
    _runJustOnceAtTheEnd.cancel();
    _runJustOnceAtTheEnd = null;
  }

  // do your processing

  _runJustOnceAtTheEnd = Timer(Duration(seconds: 1), onceAtTheEndOfTheBatch);
}

void onceAtTheEndOfTheBatch() {
  print("just once at the end of a batch!");
}


runMultipleTimes();
runMultipleTimes();
runMultipleTimes();
runMultipleTimes();

// will print 'just once at the end of a batch' one second after last execution

runMultipleTimes()方法将被依次调用,但仅在批处理1秒后才会执行onceAtTheEndOfTheBatch

答案 5 :(得分:0)

将未来的任务从做某事&#39;做某事,除非它被取消&#39;。实现这一点的一个显而易见的方法是设置一个布尔标志,并在开始处理之前在未来的闭包中检查它,也许在处理过程中的几个点。

此外,这似乎是一个黑客,但将未来的超时设置为零似乎有效地取消了未来。

答案 6 :(得分:0)

对于那些试图在Flutter中实现这一目标的人,这是一个简单的例子。

class MyPage extends StatelessWidget {
  final CancelableCompleter<bool> _completer = CancelableCompleter(onCancel: () => false);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Future")),
      body: Column(
        children: <Widget>[
          RaisedButton(
            child: Text("Submit"),
            onPressed: () async {
              // it is true only if the future got completed
              bool _isFutureCompleted = await _submit();
            },
          ),
          RaisedButton(child: Text("Cancel"), onPressed: _cancel),
        ],
      ),
    );
  }

  Future<bool> _submit() async {
    _completer.complete(Future.value(_solve()));
    return _completer.operation.value;
  }

  // This is just a simple method that will finish the future in 5 seconds
  Future<bool> _solve() async {
    return await Future.delayed(Duration(seconds: 5), () => true);
  }

  void _cancel() async {
    var value = await _completer.operation.cancel();
    // if we stopped the future, we get false
    assert(value == false);
  }
}

答案 7 :(得分:0)

以下代码有助于设计将来的超时功能,可以手动将其取消。

import 'dart:async';

class API {
  Completer<bool> _completer;
  Timer _timer;

  // This function returns 'true' only if timeout >= 5 and
  // when cancelOperation() function is not called after this function call.
  //
  // Returns false otherwise
  Future<bool> apiFunctionWithTimeout() async {
    _completer = Completer<bool>();
    // timeout > time taken to complete _timeConsumingOperation() (5 seconds)
    const timeout = 6;

    // timeout < time taken to complete _timeConsumingOperation() (5 seconds)
    // const timeout = 4;

    _timeConsumingOperation().then((response) {
      if (_completer.isCompleted == false) {
        _timer?.cancel();
        _completer.complete(response);
      }
    });

    _timer = Timer(Duration(seconds: timeout), () {
      if (_completer.isCompleted == false) {
        _completer.complete(false);
      }
    });

    return _completer.future;
  }

  void cancelOperation() {
    _timer?.cancel();
    if (_completer.isCompleted == false) {
      _completer.complete(false);
    }
  }

  // this can be an HTTP call.
  Future<bool> _timeConsumingOperation() async {
    return await Future.delayed(Duration(seconds: 5), () => true);
  }
}

void main() async {
  API api = API();
  api.apiFunctionWithTimeout().then((response) {
    // prints 'true' if the function is not timed out or canceled, otherwise it prints false
    print(response);
  });
  // manual cancellation. Uncomment the below line to cancel the operation.
  //api.cancelOperation();
}

返回类型可以从bool更改为您自己的数据类型。 Completer对象也应相应更改。

答案 8 :(得分:0)

pub.dev上的CancelableOperation中有一个async package,您现在可以使用它来执行此操作。此软件包不要与内置dart核心库dart:async混淆,该库没有此类。

答案 9 :(得分:0)

一个用于注销未来回调的小类。此类不会阻止执行,但可以在您需要切换到具有相同类型的另一个未来时提供帮助。不幸的是我没有测试它,但是:

class CancelableFuture<T> {
  Function(Object) onErrorCallback;
  Function(T) onSuccessCallback;
  bool _wasCancelled = false;

  CancelableFuture(Future<T> future,
      {this.onSuccessCallback, this.onErrorCallback}) {
    assert(onSuccessCallback != null || onErrorCallback != null);
    future.then((value) {
      if (!_wasCancelled && onSuccessCallback != null) {
        onSuccessCallback(value);
      }
    }, onError: (e) {
      if (!_wasCancelled && onErrorCallback != null) {
        onErrorCallback(e);
      }
    });
  }

  cancel() {
    _wasCancelled = true;
  }
}

这是使用示例。附言我在我的项目中使用提供程序:

_fetchPlannedLists() async {
    if (_plannedListsResponse?.status != Status.LOADING) {
      _plannedListsResponse = ApiResponse.loading();
      notifyListeners();
    }

    _plannedListCancellable?.cancel();

    _plannedListCancellable = CancelableFuture<List<PlannedList>>(
        _plannedListRepository.fetchPlannedLists(),
        onSuccessCallback: (plannedLists) {
      _plannedListsResponse = ApiResponse.completed(plannedLists);
      notifyListeners();
    }, onErrorCallback: (e) {
      print('Planned list provider error: $e');
      _plannedListsResponse = ApiResponse.error(e);
      notifyListeners();
    });
  }

您可以在以下情况下使用它:当语言发生变化并提出请求时,您不必关心先前的响应并提出另一个请求! 另外,我真的很奇怪这个功能不是自带的。