중복 제출 다루기

Single Flight And Duplicate Submission

Apply an explicit policy to repeated activation of one pending operation.

GitHub에서 원문 보기 ↗새 탭
원문 가이드 ENGLISH

Repository Boundary

Apply an explicit policy to repeated activation of one pending operation. This pattern belongs to Design Engineering and adds no Layout CSS or shared store implementation.

Reusable Method

Use when a duplicate Save or Delete should share or ignore the active attempt. Independent entities may need separate flight keys. A queue is preferable when every intent must execute.

State Model And Ownership

The operation owner holds one pending promise per declared scope. This example shares it. The guard is set before the effect begins and clears on success or failure.

Transitions

Event and precondition Next state Effect or invariant
Activate while idle One pending operation Run effect once
Activate while pending Same pending operation Return same promise
Settle Idle Release guard on success and failure

Minimal Executable Example

Run this standalone JavaScript block with Node.js 22 or newer, or run all pattern examples from a repository checkout with npm run test:state-management. The assertions exercise the local model, not a browser or backend.

import assert from "node:assert/strict";
let pending = null, calls = 0;
function run(effect) {
  if (pending) return pending;
  pending = Promise.resolve().then(effect).finally(() => { pending = null; });
  return pending;
}
let release;
const effect = () => { calls++; return new Promise(resolve => { release = resolve; }); };
const a = run(effect), b = run(effect);
assert.equal(a, b);
await Promise.resolve();
assert.equal(calls, 1);
release("saved");
await a;
await assert.rejects(run(() => { throw new Error("offline"); }));
assert.equal(pending, null);
assert.equal(await run(() => "retry"), "retry");

What Breaks If Removed

Disabling only the visible button misses keyboard submission or another caller. Setting pending after awaiting the effect permits two operations. Clearing only on success locks the action after a failure.

Composition And Substitution

Use with submitted snapshot; duplicate activation must not silently replace the captured snapshot. For separate rows, scope the guard by stable entity ID.

Opinionated Guidance

Pick ignore, share, queue, or replace deliberately. This example shares a result and does not implement queuing. After an ambiguous write failure, reconcile or use server-supported idempotency before retrying.

Platform-Specific Guidance

Handle every caller's rejection and expose progress through accessible status. UI disabled semantics do not replace the operation guard.

Unsupported Absolutes

One client promise cannot prevent another tab, device, or retry from executing the same server command.

Verification Contract

  • Activate from two entry points in the same turn; effect count remains one.
  • Fail the operation; a subsequent explicit attempt can start.
  • For per-entity scope, verify different entities do not block each other.

Record actual outcomes separately using the verification matrix.

Source, License, And Attribution

Locally authored synthesis and examples. No upstream implementation is copied. These experimental contracts describe consumer-owned behavior; review when a failure, ownership change, or platform change invalidates an assumption.

IA Navigation

Parent: State Management. Next: Continue the state management route.