Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

If a reactive view updates immediately for every property assignment, several synchronous state changes can trigger several update calls. A simple batching pattern collects top-level changes and schedules one update with queueMicrotask(). It can avoid redundant work within that batch, but it is not a transaction system or a guarantee against every visible flicker.

Why several assignments can cause repeated view updates

In a Proxy-based template system, a set trap can detect when code assigns a new value to a state property. If that trap calls the view’s update function immediately, each assignment triggers an update of its own.

$scope.name = "Mario";
$scope.age = 30;
$scope.city = "Rome";

With an immediate-update handler, those three synchronous assignments can result in three update calls. The view may do work between assignments even though the controller is still preparing the state it intends to show.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

How batching avoids an intermediate update

Consider code that first assigns "Mario" to $scope.name and then assigns a longer, computed name. An immediate update can render the first value before the second assignment triggers another update. That intermediate display is a possible visual issue in this illustrative example, not a measured or inevitable flicker.

Instead of updating at each assignment, the system can merge the changes and call the update function once after the current JavaScript task. If the same key is assigned more than once before the queued callback runs, the later value overwrites the earlier value in the pending changes.

What the microtask schedule does

queueMicrotask() queues a callback to run after the current task has completed and before control returns to the browser event loop. MDN notes that microtasks can be useful for work that needs to happen just before rendering; it does not mean that every rendering operation has completed or that a user can never see an intermediate state. The actual visual result depends on the surrounding code and rendering behavior. See MDN’s queueMicrotask() reference and MDN’s guide to microtasks.

Implement a small top-level change batch

The scheduler below keeps changes in pendingChanges. Object.assign() merges each new change into that object, and isScheduled ensures that only one microtask is queued while a batch is waiting.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
let pendingChanges = {};
let isScheduled = false;

function scheduleUpdate(change, updateFn) {
  Object.assign(pendingChanges, change);

  if (isScheduled) return;

  isScheduled = true;
  queueMicrotask(() => {
    updateFn(pendingChanges);
    pendingChanges = {};
    isScheduled = false;
  });
}

Call scheduleUpdate() from the Proxy’s set handler instead of calling the template update function immediately. The exact handler depends on how the template system represents its state and update function; the essential change is to pass the changed top-level property to the scheduler.

const scope = new Proxy(initialState, {
  set(target, key, value) {
    target[key] = value;
    scheduleUpdate({ [key]: value }, update);
    return true;
  }
});

For synchronous assignments that arrive before the scheduled callback executes, the first call queues the microtask and subsequent calls only merge their changes. When the callback runs, updateFn receives the merged object, then the scheduler clears the pending changes and its scheduling flag.

What this pattern does not handle

This example observes assignments to top-level properties intercepted by the Proxy. It does not, by itself, detect a mutation inside a nested object or an array. For example, assigning $scope.person.name = "Mario" changes a property on the nested object; it does not assign a new value to the top-level person property intercepted by this Proxy. Nested-object and array tracking need additional handling.

The batching pattern also is not a general transaction: it merges the changes captured before its callback runs, but it does not provide rollback, isolation, or broader guarantees about when a view becomes visible.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Check whether your handler is batching

For a quick diagnostic, count calls to the update function while making several synchronous top-level assignments. With immediate updates, the count can rise once per assignment; with the scheduler above, changes that arrive before its queued callback executes should be delivered together in one update call. This checks the behavior of your implementation, not its rendering performance.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.