0

I'm trying to make a observer pattern in JavaScript (One might exist in the standard library but I want to implement my own).

I need a container of weak references in order to make sure that the listeners do not keep unused functions loaded. I can use a WeakSet to do this, however a WeakSet cannot be iterated through, thus I cannot do for (let l in listeners) and inform my listeners of a change.

What can I do to solve this? In other languages such as Lua, Java, C, etc I have no problems with this.

I'm not using any external libraries.

const makeObservable = function(obj, value)
{
    // Must be a container of weak references.
    // But a weakset is not sufficient.
    const listeners = new Set();

    obj.addListener = function(listener)
    {
        listeners.add(listener);
    };

    obj.removeListener = function(listener)
    {
        listeners.remove(listener);
    };

    obj.setValue = function(newVal)
    {
        if (value === newVal) return;
        const oldVal = value;
        value = newVal;
        for (let l of listeners)
        {
            // Notify all listeners of the change.
            l(oldVal, newVal);
        }
    };

    obj.getValue = function()
    {
        return value;
    };

    return obj;
};

const obs1 = makeObservable({ }, 5);
const obs2 = makeObservable({ }, 10);

(function()
{
    const memory = { };
    const lis = function(_, newVal)
    {
        memory.changed = newVal;
    };
    // Whenever either changes, update memory.
    obs1.addListener(lis);
    obs2.addListener(lis);
})();

// 'memory' is out of scope, but still has references pointing to it.
// I have no current way of removing the memory leak until obs1 and obs2 fall out of scope.
Hatefiend
  • 3,416
  • 6
  • 33
  • 74
  • If you can iterate it, it's not a weak reference. You might want to google using the keyword "javascript cache npm" or something to that effect. WeakMaps are not the same as caches, and caches will need to be manually garbage-collected based on criteria such as LFU or LRU – Patrick Roberts May 21 '18 at 18:06
  • Other languages have iterable weak reference containers such as Lua (`x = setmetatable({ }, { __mode = "k" })` or Java's `WeakHashMap`. A LRU cache for example won't remove elements as they fall out of scope, only when a new element is added to the container. This would only mitigate memory leaks, not get rid of them. – Hatefiend May 21 '18 at 18:12
  • JS doesn't support this. – Bergi May 21 '18 at 18:13
  • @Hatefiend _Other languages have..._ JavaScript is not other languages. Bergi is correct. – Patrick Roberts May 21 '18 at 18:15
  • @PatrickRoberts Bergi Is there a way to emulate it? – Hatefiend May 21 '18 at 18:16
  • @Hatefiend no. Any attempt to create an iterable reference will _by definition_ make it a strong reference in ECMAScript specification. – Patrick Roberts May 21 '18 at 18:18
  • @PatrickRoberts I understand. I suppose this means that in order to accomplish what I have here, I'd have to be very attentive to `removeListener` to prevent memory leaks? – Hatefiend May 21 '18 at 18:19
  • User code must be responsible for explicitly calling `removeListener` before the reference to the function falls out of user code's scope. – Patrick Roberts May 21 '18 at 18:21

0 Answers0