Task Queue
Not to be confused with the task queue in Aurelia 1, the TaskQueue in Aurelia is an advanced scheduler designed to handle synchronous and asynchronous tasks efficiently. It provides a robust solution to common issues like timing problems, memory leaks, and race conditions often arising from traditional JavaScript timing functions like setTimeout
, setInterval
, and unmanaged promises.
Benefits of Using the Task Queue
Improved Performance: By managing tasks more efficiently, the TaskQueue enhances the performance of applications.
Synchronous and Asynchronous Support: It supports both synchronous and asynchronous tasks, providing greater flexibility in handling tasks.
Deterministic Task Execution: Facilitates testing by providing deterministic ways to wait for task completion, reducing test flakiness.
Avoids Common Pitfalls: Helps avoid common issues associated with
setTimeout
andsetInterval
, such as memory leaks and race conditions.
Examples and Use Cases
Replacing setTimeout
(Synchronous)
setTimeout
(Synchronous)Instead of `setTimeout, ' the TaskQueue offers a more reliable way to queue tasks without delay.
import { PLATFORM } from 'aurelia';
const task = PLATFORM.taskQueue.queueTask(() => {
// Task to be executed after the delay
doStuff();
}, { delay: 100 });
// Cancel the task if needed
task.cancel();
If you were to use a native setTimout
, it would look like this:
// Queue
const handle = setTimeout(() => {
doStuff();
}, 100);
// Cancel
clearTimeout(handle);
Advantages Over setTimeout
setTimeout
*Testability: You can await
PLATFORM.taskQueue.yield()
or usePLATFORM.taskQueue.flush()
in tests for predictable task execution.Improved Reliability: Reduces the chances of intermittent and hard-to-debug failures.
setTimeout (asynchronous)
For asynchronous operations, the TaskQueue can handle tasks without the issues of floating promises.
import { PLATFORM } from 'aurelia';
const task = PLATFORM.taskQueue.queueTask(async () => {
await doAsyncStuff();
}, { delay: 100 });
// Await the result of the task
await task.result;
// Cancel if necessary
task.cancel();
Implementing setInterval
The TaskQueue can mimic setInterval
functionality, offering more control and reliability.
import { PLATFORM } from 'aurelia';
const task = PLATFORM.taskQueue.queueTask(() => {
// Repeated task
poll();
}, { delay: 100, persistent: true });
// Cancel the repeating task
task.cancel();
Replacing requestAnimationFrame
For tasks that need to synchronize with the browser's repaint, domQueue
is a safer alternative to requestAnimationFrame
.
import { PLATFORM } from 'aurelia';
PLATFORM.domQueue.queueTask(() => {
// Update styles or DOM
applyStyles();
});
Animation Loop with requestAnimationFrame
For continuous animations, the TaskQueue can be used to create a loop, similar to requestAnimationFrame
.
import { PLATFORM } from 'aurelia';
const task = PLATFORM.domQueue.queueTask(() => {
// Update animation properties in each frame
updateAnimationProps();
}, { persistent: true });
// Stop the animation
task.cancel();
Modern Task Queue API
For more advanced task management, Aurelia provides dedicated functions from @aurelia/runtime
:
import { queueTask, queueAsyncTask, runTasks, tasksSettled } from '@aurelia/runtime';
// Queue a synchronous task
queueTask(() => {
console.log('Synchronous task executed');
});
// Queue an async task with full Task control
const task = queueAsyncTask(async () => {
const result = await fetchData();
return result;
}, { delay: 100 });
// Handle the task result
task.result.then(result => {
console.log('Task completed with result:', result);
}).catch(error => {
console.error('Task failed:', error);
});
// Cancel if needed
task.cancel();
Advanced Task Queue Features
Task Status and Lifecycle
Every task has a status that tracks its lifecycle:
import { PLATFORM } from 'aurelia';
const task = PLATFORM.taskQueue.queueTask(() => {
console.log('Task running');
});
console.log(task.status); // 'pending' | 'running' | 'completed' | 'canceled'
console.log(task.id); // Unique incrementing ID
console.log(task.createdTime); // When the task was created
console.log(task.queueTime); // When the task was queued
Advanced Task Options
preempt
Option
preempt
OptionThe preempt
option allows tasks to run immediately if the queue is currently processing:
// Runs synchronously if queue is currently flushing
PLATFORM.taskQueue.queueTask(() => {
console.log('Runs immediately during queue flush');
}, { preempt: true });
// Note: preempt cannot be combined with delay > 0 or persistent: true
suspend
Option
suspend
OptionThe suspend
option blocks the queue until the task completes:
// Blocks subsequent tasks until this async task completes
PLATFORM.taskQueue.queueTask(async () => {
await longRunningOperation();
console.log('Queue was suspended until this completed');
}, { suspend: true });
Multiple Queue Types
Aurelia provides different queue types for different use cases:
import { PLATFORM } from 'aurelia';
// Basic task queue (setTimeout-based)
PLATFORM.taskQueue.queueTask(() => {
console.log('Basic task');
});
// DOM queue (requestAnimationFrame-based)
PLATFORM.domQueue.queueTask(() => {
// Perfect for DOM updates and animations
element.style.opacity = '1';
});
Task Cancellation
Tasks can be canceled before or during execution:
// Cancel before execution
const task = PLATFORM.taskQueue.queueTask(() => {
console.log('This might not run');
}, { delay: 1000 });
const canceled = task.cancel(); // Returns true if successfully canceled
// Cancel persistent tasks
const persistentTask = PLATFORM.taskQueue.queueTask(() => {
if (shouldStop) {
persistentTask.cancel(); // Stops the recurring execution
}
}, { persistent: true });
Error Handling
The task queue provides sophisticated error handling:
import { PLATFORM } from 'aurelia';
import { queueTask, queueAsyncTask, runTasks, tasksSettled } from '@aurelia/runtime';
// Individual task errors
queueTask(() => {
throw new Error('Task failed');
});
// Multiple task failures are collected
queueTask(() => { throw new Error('First failure'); });
queueTask(() => { throw new Error('Second failure'); });
try {
await tasksSettled();
} catch (error) {
if (error instanceof AggregateError) {
console.log('Multiple failures:', error.errors.length);
error.errors.forEach(err => console.log(err.message));
}
}
Testing with Task Queues
The task queue provides several utilities for testing:
import { PLATFORM } from 'aurelia';
import { runTasks, tasksSettled } from '@aurelia/runtime';
// In your tests:
// 1. Flush all pending tasks synchronously
runTasks(); // Immediately executes all queued tasks
// 2. Wait for all tasks to complete (including async)
await tasksSettled();
// Note: For DOM-related tasks, use PLATFORM.domQueue for animation frame scheduling
Performance Optimization
Task Batching
// Batch multiple DOM updates
PLATFORM.domQueue.queueTask(() => {
// Multiple DOM changes in one frame
element1.style.left = '100px';
element2.style.top = '200px';
element3.textContent = 'Updated';
});
Deadlock Protection
The task queue automatically detects potential infinite loops:
// The queue will throw an error if too many tasks are queued recursively
PLATFORM.taskQueue.queueTask(function recursive() {
PLATFORM.taskQueue.queueTask(recursive); // This will eventually throw
});
Best Practices
Use the appropriate queue type:
taskQueue
for general logic and business operationsdomQueue
for DOM updates and animations
Handle async tasks properly:
Use
suspend: true
for critical async operations that must complete before other tasksUse
await task.result
to wait for individual task completion
Clean up persistent tasks:
Always keep references to persistent tasks for cancellation
Cancel persistent tasks when components are destroyed
Use testing utilities:
Use
runTasks()
for synchronous testingUse
tasksSettled()
for async task completionUse
yield()
to wait for queue idle state
Monitor performance:
Enable tracing in development to debug queue behavior
Use the DOM queue for animation-related tasks to sync with browser repaints
Last updated
Was this helpful?