跳至內容
Astera Technology

元件停用後,事件監聽也要收好

兩次固定點擊之間 abort,核對監聽器是否已移除。

下載完整個案資料及參考答案 ↓

先看這個固定例子

程式先點一次,再 abort 該監聽器的 controller,然後再點。印出的數字只記這段固定生命週期,不代表其他工作也被取消。

完整頁面內容與固定程式
<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">計本地點擊</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>
確實的 console 輸出
1
輸入與原生結果
核對項目所給輸入或動作結果
第一次點擊signal 未終止count: 1
abort()signal.abortedtrue
第二次點擊監聽器已移除count: 1

原生機制怎樣處理

signal 選項把該監聽器連到 controller,abort 後便不再接收事件。已 abort 的 signal 不會加入新監聽器;另一個仍有效的 signal 所屬監聽器可照常跑,並非全域暫停。

比較另一寫法

另外設 disposed 旗標,但回呼沒有讀它、也沒有移除,兩次固定點擊仍會累加。

另開的反例原始碼
<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">計本地點擊</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>
反例輸出
{"disposed":true,"count":2}

試清邊界

用已 abort 的 signal 加監聽器,會否執行?另用新的 controller 又如何?

查看參考答案

舊 signal 的監聽器不會跑;新監聽器先跑一次,再 abort 就擋住下一次固定點擊。

另開的邊界原始碼
<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">計本地點擊</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>
邊界輸出
{"oldCount":0,"liveCount":1,"oldAborted":true,"liveAborted":true}

原始例子檔案