ngx-task
July 25, 2026 · View on GitHub
Signal-first controlled asynchronous actions for Angular with cancellation, lifecycle cleanup, and explicit concurrency policies.
Stop rewriting loading flags, cancellation controllers, duplicate submission guards, and queue mechanics for every Angular action. ngx-task provides a single reactive primitive that explicitly answers the question:
"What should happen when this asynchronous operation is invoked again before it finishes?"
📖 Full Interactive Documentation
Explore the complete Astro Starlight documentation site:
👉 https://MahmoudAdelJR.github.io/ngx-task-suite/
| Section | Links |
|---|---|
| 🚀 Getting Started | Installation · Quick Start |
| 🧠 Concepts | Architecture · Task vs Resource / RxJS |
| 🛠️ Core API Reference | createTask() · Task Signals · TaskExecution · TaskContext |
| 🚦 Concurrency Policies | Overview · drop · restart · enqueue · latest · parallel |
| 🔌 Handlers & Adapters | Promise / AbortSignal · RxJS & HttpClient |
| ⚡ Production Features | Timeouts · Anti-Flicker · Error Classification · Progress & Retries · Lifecycle |
| 🎨 Template Directives | Directives Overview |
| 🧪 Testing Utilities | Testing Guide |
| 🔬 Advanced | Zoneless Angular |
📦 Installation
npm install ngx-task
Peer dependencies: @angular/core >= 16.0.0 · rxjs >= 7.5.0
⚡ Quick Start
import { Component, inject } from '@angular/core';
import { createTask } from 'ngx-task';
import { TaskTriggerDirective, TaskDisableWhilePendingDirective } from 'ngx-task/directives';
@Component({
selector: 'app-profile-editor',
standalone: true,
imports: [TaskTriggerDirective, TaskDisableWhilePendingDirective],
template: `
<button
type="button"
[taskTrigger]="saveProfile"
[taskArgs]="userForm"
taskDisableWhilePending
>
@if (saveProfile.pending()) { Saving… } @else { Save Profile }
</button>
@if (saveProfile.error(); as err) {
<p class="error">{{ err.message }}</p>
}
`,
})
export class ProfileEditorComponent {
private api = inject(ProfileApiService);
readonly userForm = { name: 'Alice', email: 'alice@example.com' };
readonly saveProfile = createTask(
async (profile, { signal }) => this.api.saveProfile(profile, { signal }),
{
concurrency: 'drop', // Ignore duplicate clicks while running
timeout: 15_000, // Auto-abort after 15 s
pendingDelay: 150, // No spinner flash for sub-150 ms responses
},
);
}
🚦 Concurrency Policies
| Policy | Behavior | Typical Use Case |
|---|---|---|
drop | Ignores new invocations while one is running. | Form submit · Login · Payment checkout |
restart | Cancels active execution; runs newest immediately. | Live search · Filter inputs · Tab switching |
enqueue | Runs invocations sequentially in FIFO order. | Audit logs · Sequential file uploads |
latest | Finishes active; keeps only the newest queued invocation. | Autosave · Canvas/Slider sync |
parallel | Runs up to limit executions simultaneously. | Bulk uploads · Parallel asset preloads |
🧠 Key Concepts
Task vs Angular resource / rxResource
resource: Declarative reactive data fetching driven by signal dependencies (reads).ngx-task: Imperative action triggered by user gestures (form submit, click) with explicit concurrency rules (writes/commands).
Task vs RxJS Flattening Operators
RxJS operators (exhaustMap, switchMap, concatMap, mergeMap) transform streams. ngx-task surfaces execution handles, signals (pending, running, result, error), progress, anti-flicker timing, and DestroyRef cleanup as a first-class Angular primitive — no subjects or manual subscriptions required.
Cooperative Cancellation
ngx-task provides an AbortSignal for Promise handlers and auto-calls unsubscribe() for RxJS Observables. Pass context.signal to fetch() or HttpClient to stop in-flight network I/O on cancel, timeout, or component destroy.
📂 Package Entry Points
| Import | Contents |
|---|---|
ngx-task | createTask, all signals, schedulers, state machine, and adapters |
ngx-task/directives | TaskTriggerDirective, TaskDisableWhilePendingDirective, TaskBusyDirective |
ngx-task/testing | createTaskHarness, createControlledTaskHandler, createDeferred, createTaskTestClock |
⚖️ License
MIT © Mahmoud Adel