Skip to content
Astera Technology

Dispose a local listener with an abort signal

Remove a signal-scoped click listener between two fixed clicks.

Download complete case notes and answer keys ↓

Inspect the fixed example

The script clicks once, aborts the listener’s controller, then clicks again. The printed count records this fixed lifecycle, not cancellation of other work.

Complete body and fixed script
<style>html{font-size:16px}body{margin:12px;font:1rem/1.5 system-ui,sans-serif;color:#17332d;background:#fff}p{overflow-wrap:anywhere}output{display:block;margin-block:12px;overflow-wrap:anywhere;white-space:pre-wrap}button,input{font:inherit;max-width:100%;box-sizing:border-box}button{margin:4px;padding:8px}label{display:block;margin-block:8px}button:focus-visible,input:focus-visible{outline:3px solid #23715d;outline-offset:2px}ul{padding-inline-start:24px}li{overflow-wrap:anywhere}.highlight{border:2px solid green}</style>
<button id="action" type="button">Count local click</button><output id="result"></output>
<script>let count=0;const controller=new AbortController();const action=document.querySelector('#action');action.addEventListener('click',()=>count++,{signal:controller.signal});action.click();controller.abort();action.click();
const result=count;
document.querySelector('#result').textContent=JSON.stringify(result);
console.log(JSON.stringify(result));
</script>
Exact console output
1
Inputs and native results
InspectionSupplied input or actionResult
First clickLive signalcount: 1
abort()signal.abortedtrue
Second clickListener removedcount: 1

Follow the native mechanism

The signal option associates this listener with the controller. Aborting removes it for subsequent events. An already-aborted signal does not register a new listener. Other listeners with another live signal can still run; the signal is not a global pause.

Compare the material alternative

Setting a separate disposed flag does nothing if the handler neither checks it nor gets removed. Both fixed clicks still increment.

Fresh counterexample source
<style>html{font-size:16px}body{margin:12px;font:1rem/1.5 system-ui,sans-serif;color:#17332d;background:#fff}p{overflow-wrap:anywhere}output{display:block;margin-block:12px;overflow-wrap:anywhere;white-space:pre-wrap}button,input{font:inherit;max-width:100%;box-sizing:border-box}button{margin:4px;padding:8px}label{display:block;margin-block:8px}button:focus-visible,input:focus-visible{outline:3px solid #23715d;outline-offset:2px}ul{padding-inline-start:24px}li{overflow-wrap:anywhere}.highlight{border:2px solid green}</style>
<button id="action" type="button">Count local click</button><output id="result"></output>
<script>let count=0,disposed=false;const action=document.querySelector('#action');action.addEventListener('click',()=>count++);action.click();disposed=true;action.click();
const result={disposed,count};
document.querySelector('#result').textContent=JSON.stringify(result);
console.log(JSON.stringify(result));
</script>
Counterexample output
{"disposed":true,"count":2}

Work the boundary

Will a listener registered with an already-aborted signal run? What about a separate live controller?

Show the worked answer

The old signal’s listener never runs. The separate live listener runs once, then its abort prevents the next supplied click.

Fresh boundary source
<style>html{font-size:16px}body{margin:12px;font:1rem/1.5 system-ui,sans-serif;color:#17332d;background:#fff}p{overflow-wrap:anywhere}output{display:block;margin-block:12px;overflow-wrap:anywhere;white-space:pre-wrap}button,input{font:inherit;max-width:100%;box-sizing:border-box}button{margin:4px;padding:8px}label{display:block;margin-block:8px}button:focus-visible,input:focus-visible{outline:3px solid #23715d;outline-offset:2px}ul{padding-inline-start:24px}li{overflow-wrap:anywhere}.highlight{border:2px solid green}</style>
<button id="action" type="button">Count local click</button><output id="result"></output>
<script>let oldCount=0,liveCount=0;const old=new AbortController();old.abort();const live=new AbortController();const action=document.querySelector('#action');action.addEventListener('click',()=>oldCount++,{signal:old.signal});action.addEventListener('click',()=>liveCount++,{signal:live.signal});action.click();live.abort();action.click();
const result={oldCount,liveCount,oldAborted:old.signal.aborted,liveAborted:live.signal.aborted};
document.querySelector('#result').textContent=JSON.stringify(result);
console.log(JSON.stringify(result));
</script>
Boundary output
{"oldCount":0,"liveCount":1,"oldAborted":true,"liveAborted":true}

Original example files