My question is really " expired listener issue preventable in javascript?" but apparently the word "problem" is causing the problem.
On a wikipedia page, a claimed issue with a listener can be resolved by an entity holding weak links to observers. I implemented this before in Java, and it works well, and I thought I would implement it in Javascript, but now I don’t understand how to do it. Does javascript even have weak links? I see that there is WeakSet, and WeakMapwho have a "weak" in their names, but they seem to me not useful, as far as I can see.
Here's a jsfiddle showing a typical case of a problem.
html:
<div id="theCurrentValueDiv">current value: false</div>
<button id="thePlusButton">+</button>
javascript:
'use strict';
console.log("starting");
let createListenableValue = function(initialValue) {
let value = initialValue;
let listeners = [];
return {
get: function() {
return value;
},
set: function(newValue) {
value = newValue;
for (let listener of listeners) {
listener();
}
},
addListener: function(listener) {
listeners.push(listener);
console.log("and now there "+(listeners.length==1?"is":"are")+" "+listeners.length+" listener"+(listeners.length===1?"":"s"));
},
};
};
let theListenableValue = createListenableValue(false);
theListenableValue.addListener(function() {
console.log(" label got value change to "+theListenableValue.get());
document.getElementById("theCurrentValueDiv").innerHTML = "current value: "+theListenableValue.get();
});
let nextControllerId = 0;
let thePlusButton = document.getElementById("thePlusButton");
thePlusButton.addEventListener('click', function() {
let thisControllerId = nextControllerId++;
let anotherDiv = document.createElement('div');
anotherDiv.innerHTML = '<button>x</button><input type="checkbox"> controller '+thisControllerId;
let [xButton, valueCheckbox] = anotherDiv.children;
valueCheckbox.checked = theListenableValue.get();
valueCheckbox.addEventListener('change', function() {
theListenableValue.set(valueCheckbox.checked);
});
theListenableValue.addListener(function() {
console.log(" controller "+thisControllerId+" got value change to "+theListenableValue.get());
valueCheckbox.checked = theListenableValue.get();
});
xButton.addEventListener('click', function() {
anotherDiv.parentNode.removeChild(anotherDiv);
});
document.body.insertBefore(anotherDiv, thePlusButton);
});
In this script, the observed state is a logical value, and you can add and remove flags that view and control it, all synchronized by listeners on it. The problem is that when you delete one of the controllers, its listener does not leave: the listener continues to receive calls and update the checkbox and does not allow the checkbox to be GCed, even if the checkbox is no longer in the DOM and otherwise GCable. This can be seen in the javascript console, as the listener callback displays a message to the console.
, DOM node GCable, node DOM. , DOM node , . ?
, , x, DOM, , - DOM, node, document.body.innerHTML = ''. , , , DOM , , GCable. ?