serialize — guarantee that async commands sharing a key never overlap.
Async bus only. Sync handlers run to completion synchronously and cannot
interleave, so serialization is meaningless there (and would turn a sync
dispatch into a Promise). Register on createAsyncCommandBus.
Prevents read-modify-write races on a shared resource: two accountWithdraw
for the same account, rapid cartCheckout clicks, or any handler where a
second dispatch must observe the first one's committed effect. This is
distinct from in-flight request dedup (which collapses identical requests) —
serialize queues distinct same-key commands so they apply in order.
Failure-safe: a rejected/failed command does NOT stall its lane — the next
same-key command proceeds regardless of the previous outcome. Per-key entries
are reclaimed once a lane drains, so the map never grows unbounded.
scope: 'cross-tab' extends serialization across tabs via the Web Locks API:
navigator.locks.request queues same-name requests FIFO across all same-origin
contexts and releases the lock when the handler settles (even on throw), so the
failure-safety guarantee holds across tabs too. Degrades to per-instance when
the API is absent.
constbus = createAsyncCommandBus(); bus.use(serialize({ key: (cmd) =>cmd.target.accountId, actions: ['account*'] })); // withdrawals for the same account now run strictly in order; // different accounts still run concurrently
Example
// serialize across every tab of the same origin: bus.use(serialize({ scope:'cross-tab', key: (cmd) =>cmd.target.accountId }));
serialize — guarantee that async commands sharing a key never overlap.
Async bus only. Sync handlers run to completion synchronously and cannot interleave, so serialization is meaningless there (and would turn a sync dispatch into a Promise). Register on
createAsyncCommandBus.Prevents read-modify-write races on a shared resource: two
accountWithdrawfor the same account, rapidcartCheckoutclicks, or any handler where a second dispatch must observe the first one's committed effect. This is distinct from in-flight request dedup (which collapses identical requests) — serialize queues distinct same-key commands so they apply in order.Failure-safe: a rejected/failed command does NOT stall its lane — the next same-key command proceeds regardless of the previous outcome. Per-key entries are reclaimed once a lane drains, so the map never grows unbounded.
scope: 'cross-tab'extends serialization across tabs via the Web Locks API:navigator.locks.requestqueues same-name requests FIFO across all same-origin contexts and releases the lock when the handler settles (even on throw), so the failure-safety guarantee holds across tabs too. Degrades to per-instance when the API is absent.