LearnNewsExamplesServices
Frontmatter
id6968
titleImplement Synchronous Batching for Effect Executions
stateClosed
labels
enhancement
assigneestobiu
createdAtJul 7, 2025, 3:26 AM
updatedAtJul 7, 2025, 1:17 PM
githubUrlhttps://github.com/neomjs/neo/issues/6968
authortobiu
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJul 7, 2025, 1:17 PM

Implement Synchronous Batching for Effect Executions

Closed v10.0.0-beta.5 enhancement
tobiu
tobiu commented on Jul 7, 2025, 3:26 AM

Description

This feature introduces a synchronous batching mechanism for Neo.core.Effect executions, ensuring that effects triggered by multiple reactive config changes within a single logical operation (e.g., instance.set() or direct assignments that cascade through beforeSet/afterSet hooks) are executed only once per batch. This significantly improves performance and aligns the Effect system with the framework's existing update cycles.

Motivation

Prior to this change, Neo.core.Effect instances would re-execute their functions immediately upon any change to their tracked Neo.core.Config dependencies. While correct for individual changes, this led to inefficiencies in scenarios like:

  • core.Base#set() operations: When instance.set({propA: valA, propB: valB}) was called, each individual config change (propA, propB) would trigger a separate Effect run, even though the developer intended a single, batched update.
  • Cascading beforeSet/afterSet hooks: If a config change in a beforeSet or afterSet hook triggered another config change, the Effect could run multiple times within a single synchronous call stack.

This resulted in redundant computations and potential performance bottlenecks, especially for complex components with many reactive bindings.

Changes Made

  1. Neo.core.EffectBatchManager (New Singleton: src/core/EffectBatchManager.mjs):

    • Introduced as a singleton responsible for managing the global batch state.
    • Maintains a batchCount to track nested batch operations.
    • Manages a pendingEffects Set to store Effect instances queued for execution.
    • Provides startBatch(), endBatch(), isBatchActive(), and queueEffect() methods.
  2. src/core/Effect.mjs - run() method modification:

    • The run() method of Neo.core.Effect now checks EffectBatchManager.isBatchActive().
    • If a batch is active, the Effect is added to EffectBatchManager.pendingEffects and its execution is deferred until the batch completes.
    • If no batch is active, the Effect executes its function immediately.
  3. src/Neo.mjs - autoGenerateGetSet() set() method modification:

    • This is the crucial integration point. The set() method within the public descriptor generated by autoGenerateGetSet() (which is the universal entry point for all reactive config changes) now conditionally starts and ends a batch.
    • It checks !Neo.core.EffectBatchManager?.isBatchActive(). If true, it calls EffectBatchManager.startBatch() at the beginning and EffectBatchManager.endBatch() at the end of the set() operation.
    • This ensures that any reactive config change, whether from core.Base#set() or a direct assignment, is wrapped in a batch, and any cascading changes within beforeSet/afterSet hooks participate in that same batch.
  4. test/siesta/tests/core/EffectBatching.mjs (New Test File):

    • A comprehensive test suite has been added to validate the batching behavior.
    • Tests cover:
      • Multiple config changes via instance.set() resulting in a single Effect run.
      • Individual config changes outside a batch running immediately.
      • No-operation batches not triggering Effect runs.
      • Complex scenarios involving beforeSet and afterSet hooks indirectly changing dependencies, confirming that the Effect still runs only once per batch.
      • Nested batch management by EffectBatchManager.

Benefits

  • Significant Performance Improvement: Eliminates redundant Effect executions, especially in scenarios with batched updates or cascading config changes.
  • Predictable Execution: Ensures Effects always run on the final, consistent state of their dependencies within a batch.
  • Framework Consistency: Aligns the Effect system's update cycle with the existing core.Base#set() batching pattern.
  • Robustness: Provides a more reliable and efficient foundation for all reactive data flows in Neo.mjs.
tobiu assigned to @tobiu on Jul 7, 2025, 3:26 AM
tobiu added the enhancement label on Jul 7, 2025, 3:26 AM
tobiu referenced in commit 8cbebf3 - "Implement Synchronous Batching for Effect Executions #6968" on Jul 7, 2025, 3:27 AM
tobiu closed this issue on Jul 7, 2025, 1:17 PM
tobiu referenced in commit 37660e1 - "Implement Synchronous Batching for Effect Executions #6968" on Jul 9, 2025, 2:10 AM