run()
Execute the task and return a Futurable.
Syntax
typescript
task.run(signal?: AbortSignal): Futurable<T>Parameters
signal (optional)
An external AbortSignal that can cancel this specific execution. This is combined with the task's internal signal.
Return Value
A Futurable<T> that resolves with the task's result or rejects with an error.
Description
The run() method executes the task's computation and returns a Futurable. This is where the actual work happens - creating a task is lazy, but calling run() triggers execution.
Key Behaviors
- Lazy Execution: The task only executes when
run()is called - Multiple Runs: Each call to
run()is an independent execution - Cancellable: The returned Futurable can be cancelled
- Signal Chaining: External signals are combined with the task's signal
- Memoization: If enabled, subsequent runs return cached results
Examples
Basic Usage
typescript
const task = FuturableTask.of(() => {
console.log('Executing...');
return 42;
});
// First run
const result1 = await task.run(); // Logs: "Executing..."
console.log(result1); // 42
// Second run (independent)
const result2 = await task.run(); // Logs: "Executing..." again
console.log(result2); // 42With External Signal
typescript
const task = FuturableTask.of(() => longOperation());
const controller = new AbortController();
// Run with external signal
const execution = task.run(controller.signal);
// Cancel this specific execution
setTimeout(() => controller.abort(), 1000);
try {
await execution;
} catch (error) {
console.log('Execution cancelled');
}Multiple Independent Executions
typescript
let counter = 0;
const task = FuturableTask.of(() => {
return ++counter;
});
// Run multiple times
const run1 = task.run();
const run2 = task.run();
const run3 = task.run();
const results = await Promise.all([run1, run2, run3]);
console.log(results); // [1, 2, 3] - each is independentWith Cancellation
typescript
const task = FuturableTask.of(() => {
console.log('Starting...');
return new Promise(resolve => {
setTimeout(() => resolve('Done'), 5000);
});
}).onCancel(() => {
console.log('Cleanup!');
});
const execution = task.run();
// Cancel the execution
setTimeout(() => {
execution.cancel(); // Logs: "Cleanup!"
}, 1000);Checking Task Cancellation
typescript
const task = FuturableTask.of(() => slowOperation());
// Cancel the task itself
task.cancel();
// This execution won't start
const execution = task.run();
// The execution will be in a pending state
// (won't resolve or reject)With Memoization
typescript
let executionCount = 0;
const task = FuturableTask.of(() => {
executionCount++;
console.log(`Execution ${executionCount}`);
return expensiveOperation();
}).memoize();
await task.run(); // Logs: "Execution 1"
await task.run(); // Returns cached result, no log
await task.run(); // Returns cached result, no log
console.log(executionCount); // 1Working with the Returned Futurable
Chaining
typescript
const task = FuturableTask.of(() => fetch('/api/data'));
const result = await task
.run()
.then(res => res.json())
.then(data => processData(data));Cancelling the Execution
typescript
const execution = task.run();
// Cancel this specific execution
execution.cancel();
// Or with onCancel
execution.onCancel(() => {
console.log('Execution cancelled');
});Error Handling
typescript
const task = FuturableTask.of(() => riskyOperation());
try {
const result = await task.run();
console.log('Success:', result);
} catch (error) {
console.error('Failed:', error);
}Execution Lifecycle
1. Pre-Execution Checks
typescript
const task = FuturableTask.of(() => work());
// If task is cancelled before run()
task.cancel();
const execution = task.run();
// Execution enters pending state, never starts2. During Execution
typescript
const task = FuturableTask.of((resolve, reject, { signal }) => {
const interval = setInterval(() => {
if (signal.aborted) {
clearInterval(interval);
return; // Stop work
}
doWork();
}, 100);
});
const execution = task.run();
// Cancel during execution
setTimeout(() => execution.cancel(), 500);3. Post-Execution
typescript
const task = FuturableTask.of(() => 42);
const execution = task.run();
const result = await execution;
console.log(result); // 42
// Cancelling after completion has no effect
execution.cancel();Common Patterns
Fire and Forget
typescript
// Start execution but don't wait
task.run();
// Or explicitly ignore the promise
void task.run();Parallel Execution
typescript
const task = FuturableTask.of(() => fetchData());
// Run multiple times in parallel
const executions = [
task.run(),
task.run(),
task.run()
];
const results = await Promise.all(executions);Sequential Execution
typescript
const task = FuturableTask.of(() => processItem());
// Run sequentially
for (let i = 0; i < 5; i++) {
await task.run();
}Conditional Execution
typescript
const task = FuturableTask.of(() => expensiveOperation());
let result;
if (needsData) {
result = await task.run();
} else {
result = getCachedData();
}Timeout Pattern
typescript
async function runWithTimeout<T>(
task: FuturableTask<T>,
timeout: number
): Promise<T> {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeout);
try {
return await task.run(controller.signal);
} finally {
clearTimeout(timer);
}
}
const result = await runWithTimeout(task, 5000);Retry Pattern
typescript
async function retryRun<T>(
task: FuturableTask<T>,
attempts: number
): Promise<T> {
for (let i = 0; i < attempts; i++) {
try {
return await task.run();
} catch (error) {
if (i === attempts - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
throw new Error('All retries failed');
}
const result = await retryRun(task, 3);Performance Considerations
Memoization for Expensive Operations
typescript
// ❌ Without memoization - computes every time
const expensiveTask = FuturableTask.of(() => {
return veryExpensiveComputation();
});
await expensiveTask.run(); // Slow
await expensiveTask.run(); // Slow again
// ✅ With memoization - computes once
const cachedTask = FuturableTask.of(() => {
return veryExpensiveComputation();
}).memoize();
await cachedTask.run(); // Slow (first time)
await cachedTask.run(); // Fast (cached)Cancellation for Resource Management
typescript
const task = FuturableTask.of((resolve, reject, { signal, onCancel }) => {
const resources = [];
// Allocate resources
resources.push(allocateResource1());
resources.push(allocateResource2());
// Cleanup on cancellation
onCancel(() => {
resources.forEach(r => r.cleanup());
});
// Do work with resources
performWork(resources)
.then(resolve)
.catch(reject);
});
const execution = task.run();
// If cancelled, resources are cleaned up
execution.cancel();Type Safety
typescript
// TypeScript infers the return type
const numberTask = FuturableTask.of(() => 42);
const result: number = await numberTask.run(); // Type is number
// Explicit type annotation
const userTask = FuturableTask.of(async () => {
const res = await fetch('/api/user');
return res.json();
}) as FuturableTask<User>;
const user: User = await userTask.run();Comparison with runSafe()
| Method | Returns | On Success | On Error |
|---|---|---|---|
run() | Futurable<T> | Resolves with value | Rejects |
runSafe() | Futurable<Result<T>> | { success: true, data, error: null } | { success: false, data: null, error } |
typescript
const task = FuturableTask.of(() => riskyOperation());
// Using run() - throws on error
try {
const result = await task.run();
console.log(result);
} catch (error) {
console.error(error);
}
// Using runSafe() - never throws
const result = await task.runSafe();
if (result.success) {
console.log(result.data);
} else {
console.error(result.error);
}Notes
- Each call to
run()creates a new independent execution - Memoized tasks reuse the cached result across runs
- External signals are combined (AND logic) with the task's signal
- Cancelling the task cancels all future runs
- Cancelling an execution only cancels that specific run
- The returned Futurable inherits all Promise methods
See Also
- runSafe() - Execute without throwing errors
- cancel() - Cancel the task
- memoize() - Cache execution results
- Constructor - Create tasks
- Introduction - Learn about lazy evaluation
