Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Ответ
Future в Dart по умолчанию нельзя отменить напрямую — это принципиальный дизайн. Однако существует несколько практических способов реализовать отмену асинхронной операции.
Основная проблема
Future представляет одноразовую асинхронную операцию. Нет встроенного метода cancel(), потому что Future может быть в разных состояниях:
var future = fetchData();
// Как отменить, если fetchData() уже завершается?
// Как отменить, если Future ждёт результата?
future.cancel(); // Нет такого метода!
Способ 1: Использование CancelToken (рекомендуется)
Это самый популярный и надёжный способ. Обычно реализуется через Dio HTTP клиент:
import 'package:dio/dio.dart';
class ApiService {
late Dio dio;
CancelToken? _cancelToken;
ApiService() {
dio = Dio();
}
Future<List<User>> fetchUsers() async {
_cancelToken = CancelToken();
try {
final response = await dio.get(
'https://api.example.com/users',
cancelToken: _cancelToken,
);
return (response.data as List)
.map((e) => User.fromJson(e))
.toList();
} on DioException catch (e) {
if (CancelToken.isCancel(e)) {
print('Request cancelled');
}
rethrow;
}
}
void cancelFetchUsers() {
_cancelToken?.cancel('User cancelled');
}
}
Способ 2: Собственная реализация с CancelToken
Для операций, не связанных с HTTP:
class MyCancelToken {
bool _isCancelled = false;
VoidCallback? _onCancel;
bool get isCancelled => _isCancelled;
void setOnCancel(VoidCallback callback) {
_onCancel = callback;
}
void cancel() {
_isCancelled = true;
_onCancel?.call();
}
}
class DataProcessor {
Future<List<int>> processLargeData(List<int> data) async {
final cancelToken = MyCancelToken();
try {
final result = <int>[];
for (var item in data) {
if (cancelToken.isCancelled) {
throw Exception('Processing cancelled');
}
await Future.delayed(Duration(milliseconds: 100));
result.add(item * 2);
}
return result;
} catch (e) {
print('Error: $e');
rethrow;
}
}
}
Способ 3: Использование StreamController
Для более гибкого управления:
class CancellableFutureService {
late StreamController<bool> _cancelController;
CancellableFutureService() {
_cancelController = StreamController<bool>();
}
Future<String> downloadFile(String url) async {
try {
final subscription = _cancelController.stream.listen((cancelled) {
if (cancelled) {
throw Exception('Download cancelled');
}
});
final result = await _performDownload(url);
subscription.cancel();
return result;
} catch (e) {
print('Download failed: $e');
rethrow;
}
}
Future<String> _performDownload(String url) async {
await Future.delayed(Duration(seconds: 5));
return 'File downloaded';
}
void cancelDownload() {
_cancelController.add(true);
}
void dispose() {
_cancelController.close();
}
}
Способ 4: Использование timeout для отмены по времени
Future<String> fetchWithTimeout() async {
try {
final result = await fetchData().timeout(
Duration(seconds: 5),
onTimeout: () => throw TimeoutException('Request timed out'),
);
return result;
} on TimeoutException {
print('Request was cancelled due to timeout');
rethrow;
}
}
Future<String> fetchData() async {
await Future.delayed(Duration(seconds: 10));
return 'Data';
}
Способ 5: Использование Isolates
Для cpu-intensive операций:
import 'dart:isolate';
class IsolateService {
Isolate? _isolate;
SendPort? _sendPort;
Future<void> startLongOperation() async {
final receivePort = ReceivePort();
_isolate = await Isolate.spawn(
_heavyComputation,
receivePort.sendPort,
);
_sendPort = await receivePort.first;
}
void cancelOperation() {
_isolate?.kill(priority: Isolate.immediate);
_isolate = null;
}
static void _heavyComputation(SendPort sendPort) {
var sum = 0;
for (int i = 0; i < 1000000000; i++) {
sum += i;
}
sendPort.send(sum);
}
}
Способ 6: Riverpod с AsyncValue
В современных Flutter приложениях часто используется Riverpod:
import 'package:riverpod/riverpod.dart';
final usersFutureProvider = FutureProvider<List<User>>((ref) async {
final cancelToken = CancelToken();
ref.onDispose(() {
cancelToken.cancel();
});
return fetchUsers(cancelToken);
});
class UserListPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final usersAsync = ref.watch(usersFutureProvider);
return usersAsync.when(
data: (users) => ListView(
children: users.map((u) => Text(u.name)).toList(),
),
loading: () => CircularProgressIndicator(),
error: (err, stack) => Text('Error: $err'),
);
}
}
Лучшие практики
- Используйте CancelToken для HTTP запросов (встроено в Dio)
- Для других операций используйте собственные флаги отмены
- Всегда очищайте ресурсы в dispose() или onDispose()
- Применяйте timeout для предотвращения зависания
- В Riverpod используйте ref.onDispose() для автоматической отмены
Отмена Future — это не встроенный механизм Dart, но существует множество проверенных паттернов для её реализации.