Skip to content

Commit 4c39706

Browse files
fix: resolve memory and runtime leaks by managing all timeouts in ModeWidget (#6665)
1 parent 9f3c369 commit 4c39706

File tree

1 file changed

+35
-14
lines changed

1 file changed

+35
-14
lines changed

js/widgets/modewidget.js

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ class ModeWidget {
101101
this.widgetWindow.clear();
102102
this.widgetWindow.show();
103103

104+
this._timeouts = [];
105+
104106
// The mode table (holds a pie menu and a label)
105107
this.modeTableDiv = document.createElement("div");
106108
this.modeTableDiv.style.display = "inline";
@@ -113,6 +115,10 @@ class ModeWidget {
113115
this.widgetWindow.getWidgetBody().append(this.modeTableDiv);
114116

115117
this.widgetWindow.onclose = () => {
118+
if (this._timeouts) {
119+
this._timeouts.forEach(id => clearTimeout(id));
120+
this._timeouts = [];
121+
}
116122
this._playing = false;
117123
this.hideMsgs();
118124
this.widgetWindow.destroy();
@@ -194,7 +200,22 @@ class ModeWidget {
194200

195201
//.TRANS: A circle of notes represents the musical mode.
196202
activity.textMsg(_("Click in the circle to select notes for the mode."), 3000);
197-
setTimeout(() => this.widgetWindow.sendToCenter(), 0);
203+
this._setTimeout(() => this.widgetWindow.sendToCenter(), 0);
204+
}
205+
206+
/**
207+
* @private
208+
* @param {Function} fn - function to execute
209+
* @param {number} delay - delay in milliseconds
210+
* @returns {number} timeout ID
211+
*/
212+
_setTimeout(fn, delay) {
213+
const id = setTimeout(() => {
214+
this._timeouts = this._timeouts.filter(t => t !== id);
215+
fn();
216+
}, delay);
217+
this._timeouts.push(id);
218+
return id;
198219
}
199220

200221
/**
@@ -258,7 +279,7 @@ class ModeWidget {
258279
svg.style.pointerEvents = "none";
259280
svg.setAttribute("height", `${400 * scale}px`);
260281
svg.setAttribute("width", `${400 * scale}px`);
261-
setTimeout(() => {
282+
this._setTimeout(() => {
262283
svg.style.pointerEvents = "auto";
263284
}, 100);
264285
}
@@ -427,7 +448,7 @@ class ModeWidget {
427448
}
428449
this._locked = false;
429450
} else {
430-
setTimeout(() => {
451+
this._setTimeout(() => {
431452
this.__invertOnePair(i + 1);
432453
}, ModeWidget.ROTATESPEED);
433454
}
@@ -480,7 +501,7 @@ class ModeWidget {
480501
}
481502

482503
if (i === 0) {
483-
setTimeout(() => {
504+
this._setTimeout(() => {
484505
if (this._selectedNotes[0]) {
485506
// We are done.
486507
this._saveState();
@@ -499,7 +520,7 @@ class ModeWidget {
499520
}
500521
}, ModeWidget.ROTATESPEED);
501522
} else {
502-
setTimeout(() => {
523+
this._setTimeout(() => {
503524
this.__rotateRightOneCell((i + 1) % 12);
504525
}, ModeWidget.ROTATESPEED);
505526
}
@@ -541,7 +562,7 @@ class ModeWidget {
541562
}
542563

543564
if (i === 0) {
544-
setTimeout(() => {
565+
this._setTimeout(() => {
545566
if (this._selectedNotes[0]) {
546567
// We are done.
547568
this._saveState();
@@ -560,7 +581,7 @@ class ModeWidget {
560581
}
561582
}, ModeWidget.ROTATESPEED);
562583
} else {
563-
setTimeout(() => {
584+
this._setTimeout(() => {
564585
this.__rotateLeftOneCell(i - 1);
565586
}, ModeWidget.ROTATESPEED);
566587
}
@@ -647,7 +668,7 @@ class ModeWidget {
647668
const currentKey = keySignatureToMode(this.turtles.ithTurtle(0).singer.keySignature)[0];
648669
if (currentKey === "C") {
649670
if (i > this._notesToPlay.length - 1) {
650-
setTimeout(() => {
671+
this._setTimeout(() => {
651672
// Did we just play the last note?
652673
this._playing = false;
653674
const note_key = this._pianoKeys ? this._pianoKeys[0] : null;
@@ -669,7 +690,7 @@ class ModeWidget {
669690
return;
670691
}
671692

672-
setTimeout(() => {
693+
this._setTimeout(() => {
673694
if (this._lastNotePlayed !== null) {
674695
this._playWheel.navItems[this._lastNotePlayed % 12].navItem.hide();
675696
const note_key = this._pianoKeys
@@ -707,13 +728,13 @@ class ModeWidget {
707728
this.__playNextNote(i + 1);
708729
} else {
709730
this._locked = false;
710-
setTimeout(() => this._resetNotes(), ModeWidget.RESET_NOTES_DELAY);
731+
this._setTimeout(() => this._resetNotes(), ModeWidget.RESET_NOTES_DELAY);
711732
return;
712733
}
713734
}, 1000 * time);
714735
} else {
715736
if (i > this._notesToPlay.length - 1) {
716-
setTimeout(() => {
737+
this._setTimeout(() => {
717738
// Did we just play the last note?
718739
this._playing = false;
719740
this._playButton.innerHTML = `&nbsp;&nbsp;<img
@@ -731,7 +752,7 @@ class ModeWidget {
731752
return;
732753
}
733754

734-
setTimeout(() => {
755+
this._setTimeout(() => {
735756
if (this._lastNotePlayed !== null) {
736757
this._playWheel.navItems[this._lastNotePlayed % 12].navItem.hide();
737758
}
@@ -754,7 +775,7 @@ class ModeWidget {
754775
this.__playNextNote(i + 1);
755776
} else {
756777
this._locked = false;
757-
setTimeout(() => this._resetNotes(), ModeWidget.RESET_NOTES_DELAY);
778+
this._setTimeout(() => this._resetNotes(), ModeWidget.RESET_NOTES_DELAY);
758779
return;
759780
}
760781
}, 1000 * time);
@@ -1075,7 +1096,7 @@ class ModeWidget {
10751096

10761097
// Create a new stack for the chunk.
10771098
// console.debug(newStack);
1078-
setTimeout(() => {
1099+
this._setTimeout(() => {
10791100
this.blocks.loadNewBlocks(newStack);
10801101
}, 2000);
10811102
}

0 commit comments

Comments
 (0)