fix(core): always remove DOM listeners and stream subscriptions

This is needed to prevent memory leaks. The DOM
listeners don’t need to be removed for simple examples,
but a big internal app shows memory leaks because of them.

BREAKING CHANGE:
- `Renderer.listen` now has to return a function that
  removes the event listener.
This commit is contained in:
Tobias Bosch
2016-01-25 14:47:25 -08:00
parent 5f0baaac73
commit 0ae77753f3
15 changed files with 104 additions and 46 deletions

View File

@ -66,12 +66,12 @@ export class MessageBasedRenderer {
bind(this._invokeElementMethod, this));
broker.registerMethod("setText", [RenderStoreObject, RenderStoreObject, PRIMITIVE],
bind(this._setText, this));
broker.registerMethod("listen", [RenderStoreObject, RenderStoreObject, PRIMITIVE],
broker.registerMethod("listen", [RenderStoreObject, RenderStoreObject, PRIMITIVE, PRIMITIVE],
bind(this._listen, this));
broker.registerMethod("listenGlobal", [RenderStoreObject, PRIMITIVE, PRIMITIVE, PRIMITIVE],
bind(this._listenGlobal, this));
broker.registerMethod("listenGlobalDone", [RenderStoreObject, RenderStoreObject],
bind(this._listenGlobalDone, this));
broker.registerMethod("listenDone", [RenderStoreObject, RenderStoreObject],
bind(this._listenDone, this));
}
private _renderComponent(renderComponentType: RenderComponentType, rendererId: number) {
@ -155,9 +155,11 @@ export class MessageBasedRenderer {
renderer.setText(renderNode, text);
}
private _listen(renderer: Renderer, renderElement: any, eventName: string) {
renderer.listen(renderElement, eventName, (event) => this._eventDispatcher.dispatchRenderEvent(
renderElement, null, eventName, event));
private _listen(renderer: Renderer, renderElement: any, eventName: string, unlistenId: number) {
var unregisterCallback = renderer.listen(renderElement, eventName,
(event) => this._eventDispatcher.dispatchRenderEvent(
renderElement, null, eventName, event));
this._renderStore.store(unregisterCallback, unlistenId);
}
private _listenGlobal(renderer: Renderer, eventTarget: string, eventName: string,
@ -168,5 +170,5 @@ export class MessageBasedRenderer {
this._renderStore.store(unregisterCallback, unlistenId);
}
private _listenGlobalDone(renderer: Renderer, unlistenCallback: Function) { unlistenCallback(); }
private _listenDone(renderer: Renderer, unlistenCallback: Function) { unlistenCallback(); }
}

View File

@ -215,10 +215,18 @@ export class WebWorkerRenderer implements Renderer, RenderStoreObject {
[new FnArg(renderNode, RenderStoreObject), new FnArg(text, null)]);
}
listen(renderElement: WebWorkerRenderNode, name: string, callback: Function) {
listen(renderElement: WebWorkerRenderNode, name: string, callback: Function): Function {
renderElement.events.listen(name, callback);
this._runOnService('listen',
[new FnArg(renderElement, RenderStoreObject), new FnArg(name, null)]);
var unlistenCallbackId = this._rootRenderer.allocateId();
this._runOnService('listen', [
new FnArg(renderElement, RenderStoreObject),
new FnArg(name, null),
new FnArg(unlistenCallbackId, null)
]);
return () => {
renderElement.events.unlisten(name, callback);
this._runOnService('listenDone', [new FnArg(unlistenCallbackId, null)]);
};
}
listenGlobal(target: string, name: string, callback: Function): Function {
@ -229,7 +237,7 @@ export class WebWorkerRenderer implements Renderer, RenderStoreObject {
[new FnArg(target, null), new FnArg(name, null), new FnArg(unlistenCallbackId, null)]);
return () => {
this._rootRenderer.globalEvents.unlisten(eventNameWithTarget(target, name), callback);
this._runOnService('listenGlobalDone', [new FnArg(unlistenCallbackId, null)]);
this._runOnService('listenDone', [new FnArg(unlistenCallbackId, null)]);
};
}
}