Timers และ Memory Leak
setTimeout() และ setInterval() เป็นสาเหตุ Memory Leak บ่อยที่สุด Object ที่ผูกกับ Timer ไม่ถูก GC จน Timer Expire/Clear
ตัวอย่างปัญหา
<script>
for (var i = 0; i < 100000; i++) {
var tiedObject = {
callAgain: function() {
var self = this;
var timerId = setTimeout(function() {
self.callAgain(); // Closure captures tiedObject
}, 100000);
}
};
tiedObject.callAgain();
tiedObject = null; // Variable null แต่ Timer ยัง Reference อยู่!
}
</script>
ผล: 100,000 Timer + Object ค้างใน Memory
วิธีป้องกัน
1. Clear Timer เมื่อไม่ใช้
const timerId = setInterval(() => { /* ... */ }, 1000);
// ตอน Cleanup:
clearInterval(timerId);
2. ใช้ WeakRef/FinalizationRegistry (ES2021)
const registry = new FinalizationRegistry(heldValue => {
console.log('Cleanup:', heldValue);
});
function createTimer(obj) {
const timerId = setInterval(() => obj.doWork(), 1000);
registry.register(obj, timerId, obj); // Register Cleanup
return timerId;
}
3. AbortController Pattern (Modern)
const controller = new AbortController();
const signal = controller.signal;
setTimeout(() => {
if (!signal.aborted) doWork();
}, 1000);
// Cancel:
controller.abort();
4. Library จัดการให้ (jQuery, RxJS, Lodash)
Modern Libraries จัดการ Cleanup เอง แต่ Custom Code ต้องระวัง
Best Practices
- เก็บ Timer ID ไว้เสมอ:
const id = setInterval(...) - Clear ใน Cleanup/Unmount (React: useEffect Return, Vue: onUnmounted)
- หลีกเลี่ยง Anonymous Function ใน Timer (ยาก Clear)
- ใช้
setTimeoutแทนsetIntervalสำหรับ Recurring (หลีกเลี่ยง Drift)