0

I'm looking for a way to trigger an event when an object is added to a Map or Set? Something like this, is it possible? Right now we're just using a timer to check.

let mymap = new Map() // or Set()
mymap.on('set', obj => {
    console.log(obj) 
}

This is my first pass based on @PsychoX's recommendations, thoughts?

const EventEmitter = require('events')
const emitter = new EventEmitter()

const Wrapper = () => {
    this._set = new Set()
    this.get = key => { return this._set.get(key) }
    this.add = obj => {
        this._set.add(obj)
        emitter.emit('add', obj)
    }
    this.values = () => { return this._set.values() }
}

emitter.on('add', obj => {
    console.log('added', obj)
})

let test = new Wrapper()
test.add({hello:'world'})
Bill Kervaski
  • 542
  • 6
  • 17

1 Answers1

1

You can extend the default Map or use proxy.

My approach:

class ObservableMapChangeEvent extends Event {
    constructor(observable, key, oldValue, newValue) {
        super('change');
        this.observable = observable;
        this.key = key;
        this.oldValue = oldValue;
        this.newValue = newValue;
    }
}

class ObservableMap extends Map {
    constructor(iterable) {
        super(iterable);
        this._eventTarget = new EventTarget();
    }

    on(name, listener, options) {
        this._eventTarget.addEventListener(name, listener, options);
    }

    off(name, listener, options) {
        this._eventTarget.addEventListener(name, listener, options);
    }

    delete(key) {
        this._eventTarget.dispatchEvent(new ObservableMapChangeEvent(this, key, this.get(key), undefined));
        super.delete(key);
    }
    set(key, value) {
        this._eventTarget.dispatchEvent(new ObservableMapChangeEvent(this, key, this.get(key), value));
        super.set(key, value);
    }
}

// Example:
const foo = new ObservableMap();
foo.on('change', event => {
    console.log(`Changes [${event.key}] to '${event.newValue}' (old: '${event.oldValue}')`);
});
foo.set('hello', 'world'); // Changes [hello] to 'world' (old: undefined)

You might want to look at:

AgainPsychoX
  • 1,527
  • 1
  • 16
  • 20
  • Oh, this got me going, will post my results in a bit .. thank you so much :D – Bill Kervaski Mar 22 '22 at 13:57
  • 1
    @BillKervaski Oh, I also tried to make it as for exercise :D – AgainPsychoX Mar 22 '22 at 14:09
  • Thanks! I posted my first pass using event emitters in the question, looking at your example now. This is great stuff! Love learning ... – Bill Kervaski Mar 22 '22 at 14:14
  • 1
    I updated my approach in the answer (fixed few things as i was writing it without testing). In comparison to your solution, it might be more reusable. However, I just now realized, you tagged `node js` which I overlooked, focusing making it work on browser. – AgainPsychoX Mar 22 '22 at 14:18
  • Totally awesome, big thanks!!! – Bill Kervaski Mar 22 '22 at 14:21
  • 1
    Huh, I today learnt that Node also has `EventEmitter`, so the solution will work for both browser and Node :) – AgainPsychoX Mar 22 '22 at 14:27
  • Yep, that's what I'm testing with and it's working great. You really opened my eyes on a few things, self taught so I have gaps :D if you had a tip jar ... – Bill Kervaski Mar 22 '22 at 14:31