在Dart用户界面中,我有一个按钮[submit]来启动长异步请求。 [submit]处理程序返回Future。接下来,按钮[submit]被按钮[cancel]替换,以允许取消整个操作。在[取消]处理程序中,我想取消长操作。如何取消提交处理程序返回的Future?我找不到办法做到这一点。
答案 0 :(得分:5)
您可以使用CancelableOperation或CancelableCompleter取消未来。请参见以下2个版本:
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
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.value
或await completer.operation
,它将永远不会返回结果,并且如果操作被取消,它将无限期地等待。这是因为await cancellableOperation.value
与写入cancellableOperation.value.then(...)
相同,但是如果操作被取消,则永远不会调用then()
。
答案 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();
});
}
您可以在以下情况下使用它:当语言发生变化并提出请求时,您不必关心先前的响应并提出另一个请求! 另外,我真的很奇怪这个功能不是自带的。