Cancellable link
What it is link
cancellable() wraps a function so that each invocation can be cancelled before it completes. Cancellation is cooperative: the wrapped future completes with a CancelException, while any inner work that has already started continues until it finishes on its own.
When to use it link
- Network requests that should be dropped when a user leaves a screen.
- Background computations that become obsolete after a newer request arrives.
- Any async work where the caller needs the ability to say “I no longer care about the result”.
Async / sync support link
| Wrapper | Support |
|---|
Func<R> | ✅ Async |
Func1<T, R> | ✅ Async |
Func2<T1, T2, R> | ✅ Async |
FuncSync<R> | ❌ No |
API reference link
1
2
3
4
5
6
7
8
9
| // api-reference
// On Func<R>
CancellableFunc<R> cancellable({CancelToken? token});
// On Func1<T, R>
CancellableFunc1<T, R> cancellable({CancelToken? token});
// On Func2<T1, T2, R>
CancellableFunc2<T1, T2, R> cancellable({CancelToken? token});
|
Returned wrapper methods link
1
2
3
4
5
6
| // Func / Func1 / Func2 variants
Future<R> call(...); // Normal call, returns result future.
CancelableOperation<R> operation(); // Func variant.
CancelableOperation<R> operation(T arg); // Func1 variant.
CancelableOperation<R> operation(T1 a, T2 b); // Func2 variant.
void cancel(); // Cancels the most recent operation.
|
CancelableOperation link
1
2
3
4
5
| final cancellableFunc = Func<String>(() async => 'data').cancellable();
final operation = cancellableFunc.operation();
operation.cancel();
await operation.value; // throws CancelException if cancelled
|
CancelToken link
1
2
3
4
5
6
7
8
9
10
11
| Future<String> workA() async => 'A';
Future<String> workB() async => 'B';
final token = CancelToken();
final a = Func(workA).cancellable(token: token);
final b = Func(workB).cancellable(token: token);
unawaited(a());
unawaited(b());
token.cancel(); // cancels both active operations
|
Parameters link
| Parameter | Type | Default | Description |
|---|
token | CancelToken? | null | Optional shared token for cancelling multiple operations at once. |
Examples link
Basic example link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| var executed = false;
final slow = Func<String>(() async {
await Future<void>.delayed(Duration(seconds: 5));
executed = true;
return 'done';
}).cancellable();
final operation = slow.operation();
operation.cancel();
try {
await operation.value;
} on CancelException {
print('cancelled');
}
print(executed); // may still become true later
|
Shared token example link
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| final token = CancelToken();
final fetchUser = Func1<String, String>((id) async {
await Future<void>.delayed(Duration(seconds: 2));
return 'user:$id';
}).cancellable(token: token);
final fetchOrders = Func1<String, String>((id) async {
await Future<void>.delayed(Duration(seconds: 2));
return 'orders:$id';
}).cancellable(token: token);
unawaited(fetchUser('42'));
unawaited(fetchOrders('42'));
token.cancel();
|
Chaining example link
1
2
3
4
5
6
7
8
9
10
| final fetch = Func<String>(() async {
await Future<void>.delayed(Duration(seconds: 2));
return 'data';
})
.retry(maxAttempts: 3)
.timeout(Duration(seconds: 5))
.cancellable();
final operation = fetch.operation();
operation.cancel();
|
Best practices link
- Place
cancellable() as the outermost decorator when chaining so cancellation wraps the entire pipeline. - Use
CancelToken when several related operations should be cancelled together, for example on widget disposal. - Catch
CancelException only when you need to react to cancellation; otherwise let it propagate to the caller. - Do not rely on cancellation to stop CPU-bound synchronous work immediately. It only prevents awaiting the result.
Common pitfalls link
- Forgetting that inner work continues: cancellation in Dart cannot literally abort a running
Future. Timers, network requests, and other async work may keep running until they complete naturally. - Using
cancellable() deep inside a chain: if placed before retry() or timeout(), the returned wrapper may not expose operation(), and cancellation may not cover the whole pipeline. - Awaiting after cancel: awaiting a cancelled operation always throws
CancelException.