Trace the fixed program
Use a local EventTarget with no DOM node. Its once listener immediately dispatches another sample event. Predict the count before the outer dispatch returns.
const target=new EventTarget();let count=0;
target.addEventListener('sample',()=>{count++;target.dispatchEvent(new Event('sample'));},{once:true});
target.dispatchEvent(new Event('sample'));
console.log(count);
1| Point in dispatch | Listener present | Count |
|---|---|---|
| Before outer callback | Removed | 0 |
| Inside outer callback | Absent for nested dispatch | 1 |
| Outer dispatch complete | Absent | 1 |
Explain the result
The once listener is removed before its callback is invoked. Nested synchronous dispatch sees it already removed. The guarantee is about this registered listener, not durable deduplication, all listeners, or a later registration.
Test the alternative
Removing at the callback’s end is too late for reentrant dispatch. This counterexample caps recursion at two callbacks; never remove the count guard while running it.
const target=new EventTarget();let count=0;
function listener(){count++;if(count<2)target.dispatchEvent(new Event('sample'));target.removeEventListener('sample',listener);}
target.addEventListener('sample',listener);target.dispatchEvent(new Event('sample'));
console.log(count);
2Work the boundary case
Would dispatching again afterward call the original once listener?
Show the worked answer
No. It remains removed. A newly registered once listener can run on a later dispatch.
const target=new EventTarget();let count=0;const listener=()=>count++;
target.addEventListener('sample',listener,{once:true});target.dispatchEvent(new Event('sample'));target.dispatchEvent(new Event('sample'));
target.addEventListener('sample',listener,{once:true});target.dispatchEvent(new Event('sample'));
console.log(count);
2