import 'dart:async'; class CancelledException implements Exception { final String? reason; const CancelledException([this.reason]); @override String toString() => reason == null ? 'CancelledException' : 'CancelledException: $reason'; } class CancellationToken { final Completer _completer = Completer(); String? _reason; bool get isCancelled => _completer.isCompleted; String? get reason => _reason; Future get whenCancelled => _completer.future; void cancel([String? reason]) { if (_completer.isCompleted) return; _reason = reason; _completer.complete(); } void throwIfCancelled() { if (isCancelled) throw CancelledException(_reason); } }