raceCancel<T> function

Future<T> raceCancel<T>(
  1. Future<T> work,
  2. CancelSignal signal,
  3. T onCancel(), {
  4. Duration poll = const Duration(milliseconds: 20),
})

Race work against signal (Android raceCancel): completes with work's value, or with onCancel's the moment signal fires — whichever is first. The abandoned work keeps running but is inert: every executor checkpoints the same signal before its next live-UI action. The signal is polled every poll (CancelSignal has no listener API).

Implementation

Future<T> raceCancel<T>(
  Future<T> work,
  CancelSignal signal,
  T Function() onCancel, {
  Duration poll = const Duration(milliseconds: 20),
}) {
  if (signal.isCancelled) {
    work.ignore();
    return Future<T>.value(onCancel());
  }
  final out = Completer<T>();
  late final Timer timer;
  timer = Timer.periodic(poll, (_) {
    if (signal.isCancelled && !out.isCompleted) {
      timer.cancel();
      out.complete(onCancel());
    }
  });
  work.then((v) {
    timer.cancel();
    if (!out.isCompleted) out.complete(v);
  }, onError: (Object e, StackTrace st) {
    timer.cancel();
    if (!out.isCompleted) out.completeError(e, st);
  });
  return out.future;
}