[SOLVED] Cleaning up events

Hey folks,

If I have a number of event listeners added:

this.entity.element.on('mouseout',this.onMouseOut,this);
this.entity.element.on('mousedown',this.onMouseDown,this);
this.entity.element.on('mouseup',this.onMouseUp,this);

How can I effectively remove these on entity.destroy?

I had set up the following:

this.entity.once('destroy',this.onDestroy,this);

…with the content of onDestroy performing the following:

this.entity.element.off('mouseout',this.onMouseOut,this);
this.entity.element.off('mousedown',this.onMouseDown,this);
this.entity.element.off('mouseup',this.onMouseUp,this);

However, the ‘destroy’ event comes AFTER the entity has been destroyed, and element has been removed, so this.entity.element is null, and I can;t access it to kill those listeners.

is the equivalent of entity.element.off() called during destroy? i.e. do I even need to manually remove my listeners which were added using the PlayCanvas event API, or does the framework tidy these up automagically?

Hmm… Part of me thinks that if the element is destroyed, it no longer can fire any events so you don’t need to clean up?

You do if it is listening to an external event (e.g another entity, input etc).

I’m more worried about the event handler references (and scope object) passed into the ‘on’ function - if those stick around, then that obj isn’t going to get GC’ed

If I can’t…

this.entity.element.off('mouseout',this.onMouseOut,this);

…within entity ‘destroy’ event, then I think I have to engineer a bit of custom framework to notify everything when it is about to get 'destroy’ed - that seems like a missed opportunity (and quite a significant one)

Would they affect GC though? The element has references to it’s listeners but assuming nothing has a reference to the element, it should be safely collected by the GC? (Or have I misunderstood how the GC worked in JS?)

That said, I wonder if you can attached a callback on the destroy of the element?

(Sorry, I’m looking at the source code)

Hmm. I can’t see an event but it feels like one should be added in the entity destroy function for ‘beforedestroy’ so you can do something like this and matches the pattern for systems.

See: https://github.com/playcanvas/engine/blob/627f09ead434b23fd19d5348ae4f098aa719e901/src/framework/entity.js#L296
And systems: https://github.com/playcanvas/engine/blob/db1594b39002134c10be52583b2ce9099470dccf/src/framework/components/system.js#L87

I’m actually a little surprised that off is not called as part of the removal of the component given they do it in entity.js.

I would monkey patch Entity.prototype.destroy so it fires a beforedestroy event myself which would give you a way to remove all listeners before the entity removes the components.

1 Like

Excellent suggestions - yes I’d love to see a ‘beforedestroy’ event in there too :smiley:

Monkey patch is simple enough - this gives me what I need:

pc.Entity.prototype.destroyOld = pc.Entity.prototype.destroy;
pc.Entity.prototype.destroy = function() {
    this.fire('beforedestroy', this);
    pc.Entity.prototype.destroyOld.apply(this);
};

Thanks for your help!