Skip to content

cancel()

Cancel the futurable operation.

Syntax

typescript
futurable.cancel(): void

Parameters

None.

Return Value

void - This method doesn't return anything.

Description

The cancel() method aborts the futurable operation. When called:

  1. The internal AbortSignal is triggered
  2. All registered onCancel callbacks are executed
  3. Any ongoing async operations should be cleaned up
  4. The futurable is rejected with an AbortError (if not already settled)

Calling cancel() on an already settled (fulfilled or rejected) futurable has no effect.

Examples

Basic Cancellation

typescript
import { Futurable } from '@ndriadev/futurable';

const futurable = new Futurable((resolve, reject, { signal }) => {
  const timeout = setTimeout(() => resolve('Done'), 5000);
  
  signal.addEventListener('abort', () => {
    clearTimeout(timeout);
    reject(new Error('Operation cancelled'));
  });
});

// Cancel after 2 seconds
setTimeout(() => futurable.cancel(), 2000);

futurable
  .then(result => console.log('Success:', result))
  .catch(error => console.log('Error:', error.message)); // "Operation cancelled"

Cancel Fetch Request

typescript
const request = Futurable.fetch('https://api.example.com/large-file');

// Start download
request
  .then(response => response.blob())
  .then(blob => console.log('Downloaded:', blob.size))
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('Download cancelled');
    }
  });

// Cancel after 3 seconds
setTimeout(() => request.cancel(), 3000);

Cancel in Promise Chain

typescript
const operation = Futurable
  .fetch('/api/users')
  .then(response => response.json())
  .then(users => {
    console.log('Processing users...');
    return users.map(u => u.name);
  });

// Cancelling works anywhere in the chain
operation.cancel();

// The fetch will be aborted, preventing .then callbacks from running

Multiple Operations

typescript
const operation1 = Futurable.fetch('/api/data1');
const operation2 = Futurable.fetch('/api/data2');
const operation3 = Futurable.fetch('/api/data3');

// Cancel all
operation1.cancel();
operation2.cancel();
operation3.cancel();

Cancel with Futurable.all

typescript
const allOperations = Futurable.all([
  Futurable.fetch('/api/users'),
  Futurable.fetch('/api/posts'),
  Futurable.fetch('/api/comments')
]);

// Cancelling the combined futurable cancels all sub-operations
allOperations.cancel();

Cancel with Polling

typescript
const polling = Futurable.polling(
  () => Futurable.fetch('/api/status').then(r => r.json()),
  5000 // Poll every 5 seconds
);

// Stop polling after 30 seconds
setTimeout(() => polling.cancel(), 30000);

Common Patterns

Cleanup Pattern

typescript
const futurable = new Futurable((resolve, reject, { signal }) => {
  const resources = allocateResources();
  
  signal.addEventListener('abort', () => {
    // Clean up resources when cancelled
    resources.cleanup();
  });
  
  // Async work
  doWork(resources)
    .then(resolve)
    .catch(reject);
});

// Later
futurable.cancel(); // Resources are cleaned up

User Cancellation

typescript
// User can cancel long operation
function downloadFile(url, onProgress) {
  const download = Futurable
    .fetch(url)
    .then(response => {
      const reader = response.body.getReader();
      // Process stream...
    });
  
  // Return cancel function
  return {
    promise: download,
    cancel: () => download.cancel()
  };
}

// Usage
const { promise, cancel } = downloadFile('https://example.com/file.zip');

// Show cancel button to user
document.getElementById('cancel-btn').onclick = cancel;

Timeout Pattern

typescript
function withTimeout(futurable, timeoutMs) {
  const timeout = setTimeout(() => {
    futurable.cancel();
  }, timeoutMs);
  
  return futurable.finally(() => clearTimeout(timeout));
}

// Usage
const operation = Futurable.fetch('/api/slow-endpoint');
const withTimeout = withTimeout(operation, 5000);

withTimeout
  .then(result => console.log('Success:', result))
  .catch(error => {
    if (error.name === 'AbortError') {
      console.log('Operation timed out');
    }
  });

React Cleanup

jsx
import { useEffect } from 'react';
import { Futurable } from '@ndriadev/futurable';

function Component() {
  useEffect(() => {
    const request = Futurable
      .fetch('/api/data')
      .then(r => r.json())
      .then(setData);
    
    // Cancel on unmount
    return () => request.cancel();
  }, []);
  
  return <div>Component</div>;
}

Notes

Idempotent Operation

Calling cancel() multiple times is safe and has no additional effect:

typescript
const futurable = Futurable.sleep(1000);

futurable.cancel(); // Cancels
futurable.cancel(); // No-op
futurable.cancel(); // No-op

Already Settled

Cancelling a settled futurable has no effect:

typescript
const futurable = Futurable.resolve('value');

futurable.cancel(); // No effect, already resolved

await futurable; // Still resolves to 'value'

Synchronous

The cancel() method is synchronous - it returns immediately:

typescript
const futurable = Futurable.sleep(1000);

console.log('Before cancel');
futurable.cancel();
console.log('After cancel'); // Executes immediately

Error Type

When a futurable is cancelled, it's typically rejected with an error that has:

  • name: 'AbortError' (for fetch operations)
  • Or a custom error message
typescript
futurable
  .catch(error => {
    console.log(error.name); // 'AbortError' or similar
    console.log(error.message); // Error message
  });

See Also

Released under the MIT License.