Understanding the binding system
Learn how Aurelia's binding system balances synchronous notifications with async computed updates and how to manage state safely.
Aurelia v2 uses a hybrid binding system that combines synchronous property notifications with asynchronous computed property updates. While observable property changes trigger immediate notifications, computed properties use asynchronous updates by default to prevent common issues like state tearing.
Understanding when updates are synchronous vs. asynchronous is crucial for managing complex state changes effectively. This document explains how Aurelia's binding system works and when you might need to use tools like batch() to ensure consistency.
Before you start: Review Watching data to see how change observers trigger callbacks, and keep Task queue handy when you need to wait for async flushes during testing.
Synchronous vs Asynchronous Updates
Observable Properties: Synchronous
Regular observable properties notify changes immediately when set:
import { observable } from 'aurelia';
class User {
@observable firstName = '';
@observable lastName = '';
}
const user = new User();
// This triggers immediate notifications
user.firstName = 'John'; // Subscribers notified immediately
user.lastName = 'Doe'; // Subscribers notified immediatelyComputed Properties: Asynchronous by Default
Computed properties defer their updates to prevent state tearing:
Understanding State Tearing
State tearing occurs when multiple related state updates trigger intermediate computations with incomplete data. While Aurelia's async-by-default computed properties prevent most state tearing, you can still encounter it with synchronous computed properties.
Example: Synchronous Computed Properties Can Still Tear
Managing State Updates with Batch
Aurelia provides the batch function to handle multiple state updates efficiently. The batch function groups state changes and defers change notifications until all updates within the batch are complete. This is essential when working with synchronous computed properties or when you need atomic updates.
Fixing State Tearing with Batch
Here's how to fix the previous example using batch:
Comparing Sync vs Async Computed Properties
When to Use Sync vs Async Computed Properties
Use Async Computed Properties (Default) When:
Performance matters: Async computed properties prevent unnecessary intermediate calculations
Complex dependencies: When your computed property depends on multiple observable properties
Template bindings: Most template bindings work well with async updates
Default choice: Choose async unless you have a specific need for synchronous behavior
Use Sync Computed Properties When:
Immediate consistency required: When other code needs the computed value immediately
Simple, fast computations: When the computation is trivial and won't cause performance issues
Legacy integration: When integrating with code that expects synchronous updates
Benefits of Using Batch
Consistency: Ensures that all related state changes are processed together, avoiding premature evaluations
Performance: Reduces unnecessary recomputations by grouping state changes
Atomic updates: Makes multiple property changes appear as a single update to observers
Predictability: Controls exactly when change notifications are sent
Best Practices
Prefer async computed properties - They're safer and perform better
Use batch() for multiple related updates - Especially when updating several properties that affect the same computed properties
Await tasksSettled() in tests - Async computed properties require waiting for task completion
Only use sync computed properties when necessary - They can cause performance issues with frequent updates
Aurelia's hybrid binding system gives you the flexibility to choose the right approach for your use case. The async-by-default behavior provides safety and performance, while batch() ensures consistency when you need atomic updates.
Next steps
Dive into watching data to observe property-level changes using decorators.
Learn how Aurelia observes DOM primitives in HTML observation.
Coordinate async work with the task queue to keep UI and tests in sync.
Last updated
Was this helpful?