Skip to content

Task Cancellation

d-markey edited this page Jan 22, 2026 · 2 revisions

Squadron provides flexible mechanisms to cancel tasks, whether they are waiting in a queue or currently executing.

Depending on your needs (canceling a specific calculation, managing queue load, or implementing timeouts), you can choose between Cooperative Cancellation (using Tokens) or Task Management (using Pool APIs).


1. Cooperative Cancellation (Recommended)

This is the most robust way to handle cancellation, especially for CPU-intensive tasks. It ensures that the worker thread actually stops working, freeing resources for other tasks.

Architecture

It relies on passing a CancelationToken (from package:cancelation_token) to your service method. The worker code periodically checks this token and throws an exception if cancellation is requested.

Step 1: Modify your Service

Update your service method to accept a CancellationToken.

@SquadronMethod()
Future<void> heavyProcess(Data data, [CancellationToken? token]) async {
  for (var i = 0; i < 1000; i++) {
    // 1. Check if cancelation was requested
    // If canceled, this throws a CanceledException immediately
    token?.throwIfCanceled();
    
    // 2. Perform a chunk of work
    await performStep(i);
  }
}

Step 2: Use it from the Client

Create a token, pass it to the worker, and cancel it when needed.

// 1. Create a token source
final token = CancelableToken();

try {
  // 2. Pass the token to the worker
  // Squadron handles the cross-thread signaling automatically
  final future = worker.heavyProcess(myData, token);
  
  // ... let's say user navigates away ...
  token.cancel(); // 3. Sends signal to worker
  
  await future;
} on CanceledException {
  print('Task was canceled successfully!');
}

Advanced Tokens

  • TimeoutToken: Automatically cancels itself after a duration.

    // Cancel automatically after 5 seconds
    final token = TimeoutToken(Duration(seconds: 5));
    worker.heavyProcess(data, token);
  • CompositeToken: Combines multiple tokens.

    final userToken = CancelableToken();
    final timeoutToken = TimeoutToken(Duration(seconds: 30));
    
    // Cancels if USER cancels OR timeout expires
    final combo = CompositeToken.any([userToken, timeoutToken]);

2. Queue & Task Management (Pool API)

When using a WorkerPool, you have additional controls to manage the task queue directly on the main thread.

pool.cancel(task)

Cancels a specific task.

final task = pool.scheduleValueTask((w) => w.heavyProcess(data));

// Later...
pool.cancel(task, "User aborted");

pool.cancelAll()

Clears the entire queue and attempts to cancel all running tasks.

// Emergency stop or clearing state
pool.cancelAll("Resetting application");

How it behaves (The "Detach" vs "Stop" Trap)

The behavior of pool.cancel(task) depends entirely on the state of the task:

  1. Pending Tasks (In Queue):

    • Effect: The task is removed from the queue. It is never sent to a worker.
    • Result: This is instant and safe.
  2. Running Tasks (value tasks like Future<T>):

    • Effect: The Future on the main thread is completed immediately with a CanceledException.
    • The Trap: If you did NOT use a CancelationToken, the worker thread keeps running until it finishes the computation. It has no way to know the main thread isn't listening anymore. The result is simply discarded when it arrives.
    • Solution: Always use a CancelationToken if the task is long-running, so the worker can actually stop.
  3. Running Streams (stream tasks like Stream<T>):

    • Effect: Squadron sends a control cancellation message to the worker.
    • Result: The generator function in the worker will stop being pulled (producer stops) and the stream closes. This effectively stops the worker.

3. Comparison of Approaches

Feature token.cancel() pool.cancel(task)
Primary Use Case Stopping work (CPU/IO) Managing User Experience / Queue
Effect on Worker Stops execution (frees thread) Detaches listener (thread might stay busy)
Stream Support Yes Yes (Auto-closes stream)
Timeout Support Yes via TimeoutToken Manual management required

Summary

  1. Use CancelableToken whenever you have a long-running CPU task. This is the only way to stop a running CPU task and reclaim the worker for other jobs.
  2. Check token.throwIfCanceled() frequently in your service loops.
  3. Use pool.cancel() when you need to clear a backlog of pending items (e.g., user changed filters and previous requests are obsolete).

Clone this wiki locally