You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-12-01 04:43:29 +03:00
Merge pull request #538 from matrix-org/dbkr/reemit_use_fewer_closures
Make re-emitting events much more memory efficient
This commit is contained in:
46
src/ReEmitter.js
Normal file
46
src/ReEmitter.js
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
Copyright 2017 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module
|
||||
*/
|
||||
|
||||
export default class Reemitter {
|
||||
constructor(target) {
|
||||
this.target = target;
|
||||
|
||||
// We keep one bound event handler for each event name so we know
|
||||
// what event is arriving
|
||||
this.boundHandlers = {};
|
||||
}
|
||||
|
||||
_handleEvent(eventName, ...args) {
|
||||
this.target.emit(eventName, ...args);
|
||||
}
|
||||
|
||||
reEmit(source, eventNames) {
|
||||
for (const eventName of eventNames) {
|
||||
if (this.boundHandlers[eventName] === undefined) {
|
||||
this.boundHandlers[eventName] = this._handleEvent.bind(this, eventName);
|
||||
}
|
||||
const boundHandler = this.boundHandlers[eventName];
|
||||
|
||||
source.on(eventName, boundHandler);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ const SyncApi = require("./sync");
|
||||
const MatrixBaseApis = require("./base-apis");
|
||||
const MatrixError = httpApi.MatrixError;
|
||||
|
||||
import reEmit from './reemit';
|
||||
import ReEmitter from './ReEmitter';
|
||||
|
||||
const SCROLLBACK_DELAY_MS = 3000;
|
||||
let CRYPTO_ENABLED = false;
|
||||
@@ -115,6 +115,8 @@ try {
|
||||
function MatrixClient(opts) {
|
||||
MatrixBaseApis.call(this, opts);
|
||||
|
||||
this.reEmitter = new ReEmitter(this);
|
||||
|
||||
this.store = opts.store || new StubStore();
|
||||
|
||||
this.deviceId = opts.deviceId || null;
|
||||
@@ -364,7 +366,7 @@ MatrixClient.prototype.initCrypto = async function() {
|
||||
this._cryptoStore,
|
||||
);
|
||||
|
||||
reEmit(this, crypto, [
|
||||
this.reEmitter.reEmit(crypto, [
|
||||
"crypto.roomKeyRequest",
|
||||
"crypto.roomKeyRequestCancellation",
|
||||
]);
|
||||
@@ -3275,7 +3277,7 @@ function _PojoToMatrixEventMapper(client) {
|
||||
function mapper(plainOldJsObject) {
|
||||
const event = new MatrixEvent(plainOldJsObject);
|
||||
if (event.isEncrypted()) {
|
||||
reEmit(client, event, [
|
||||
client.reEmitter.reEmit(event, [
|
||||
"Event.decrypted",
|
||||
]);
|
||||
event.attemptDecryption(client._crypto);
|
||||
|
||||
@@ -27,7 +27,7 @@ const ContentRepo = require("../content-repo");
|
||||
const EventTimeline = require("./event-timeline");
|
||||
const EventTimelineSet = require("./event-timeline-set");
|
||||
|
||||
import reEmit from '../reemit';
|
||||
import ReEmitter from '../ReEmitter';
|
||||
|
||||
function synthesizeReceipt(userId, event, receiptType) {
|
||||
// console.log("synthesizing receipt for "+event.getId());
|
||||
@@ -106,6 +106,8 @@ function Room(roomId, opts) {
|
||||
opts = opts || {};
|
||||
opts.pendingEventOrdering = opts.pendingEventOrdering || "chronological";
|
||||
|
||||
this.reEmitter = new ReEmitter(this);
|
||||
|
||||
if (["chronological", "detached"].indexOf(opts.pendingEventOrdering) === -1) {
|
||||
throw new Error(
|
||||
"opts.pendingEventOrdering MUST be either 'chronological' or " +
|
||||
@@ -153,7 +155,7 @@ function Room(roomId, opts) {
|
||||
// all our per-room timeline sets. the first one is the unfiltered ones;
|
||||
// the subsequent ones are the filtered ones in no particular order.
|
||||
this._timelineSets = [new EventTimelineSet(this, opts)];
|
||||
reEmit(this, this.getUnfilteredTimelineSet(),
|
||||
this.reEmitter.reEmit(this.getUnfilteredTimelineSet(),
|
||||
["Room.timeline", "Room.timelineReset"]);
|
||||
|
||||
this._fixUpLegacyTimelineFields();
|
||||
@@ -490,7 +492,7 @@ Room.prototype.getOrCreateFilteredTimelineSet = function(filter) {
|
||||
}
|
||||
const opts = Object.assign({ filter: filter }, this._opts);
|
||||
const timelineSet = new EventTimelineSet(this, opts);
|
||||
reEmit(this, timelineSet, ["Room.timeline", "Room.timelineReset"]);
|
||||
this.reEmitter.reEmit(timelineSet, ["Room.timeline", "Room.timelineReset"]);
|
||||
this._filteredTimelineSets[filter.filterId] = timelineSet;
|
||||
this._timelineSets.push(timelineSet);
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
Copyright 2015, 2016 OpenMarket Ltd
|
||||
Copyright 2017 Vector Creations Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module
|
||||
*/
|
||||
|
||||
/**
|
||||
* re-emit events raised by one EventEmitter from another
|
||||
*
|
||||
* @param {external:EventEmitter} reEmitEntity
|
||||
* entity from which we want events to be emitted
|
||||
* @param {external:EventEmitter} emittableEntity
|
||||
* entity from which events are currently emitted
|
||||
* @param {Array<string>} eventNames
|
||||
* list of events to be reemitted
|
||||
*/
|
||||
export default function reEmit(reEmitEntity, emittableEntity, eventNames) {
|
||||
for (const eventName of eventNames) {
|
||||
// setup a listener on the entity (the Room, User, etc) for this event
|
||||
emittableEntity.on(eventName, function(...args) {
|
||||
// take the args from the listener and reuse them, adding the
|
||||
// event name to the arg list so it works with .emit()
|
||||
// Transformation Example:
|
||||
// listener on "foo" => function(a,b) { ... }
|
||||
// Re-emit on "thing" => thing.emit("foo", a, b)
|
||||
reEmitEntity.emit(eventName, ...args);
|
||||
});
|
||||
}
|
||||
}
|
||||
16
src/sync.js
16
src/sync.js
@@ -32,8 +32,6 @@ const utils = require("./utils");
|
||||
const Filter = require("./filter");
|
||||
const EventTimeline = require("./models/event-timeline");
|
||||
|
||||
import reEmit from './reemit';
|
||||
|
||||
const DEBUG = true;
|
||||
|
||||
// /sync requests allow you to set a timeout= but the request may continue
|
||||
@@ -100,7 +98,7 @@ function SyncApi(client, opts) {
|
||||
this._failedSyncCount = 0; // Number of consecutive failed /sync requests
|
||||
|
||||
if (client.getNotifTimelineSet()) {
|
||||
reEmit(client, client.getNotifTimelineSet(),
|
||||
client.reEmitter.reEmit(client.getNotifTimelineSet(),
|
||||
["Room.timeline", "Room.timelineReset"]);
|
||||
}
|
||||
}
|
||||
@@ -115,7 +113,7 @@ SyncApi.prototype.createRoom = function(roomId) {
|
||||
pendingEventOrdering: this.opts.pendingEventOrdering,
|
||||
timelineSupport: client.timelineSupport,
|
||||
});
|
||||
reEmit(client, room, ["Room.name", "Room.timeline", "Room.redaction",
|
||||
client.reEmitter.reEmit(room, ["Room.name", "Room.timeline", "Room.redaction",
|
||||
"Room.receipt", "Room.tags",
|
||||
"Room.timelineReset",
|
||||
"Room.localEchoUpdated",
|
||||
@@ -132,7 +130,7 @@ SyncApi.prototype.createRoom = function(roomId) {
|
||||
SyncApi.prototype.createGroup = function(groupId) {
|
||||
const client = this.client;
|
||||
const group = new Group(groupId);
|
||||
reEmit(client, group, ["Group.profile", "Group.myMembership"]);
|
||||
client.reEmitter.reEmit(group, ["Group.profile", "Group.myMembership"]);
|
||||
return group;
|
||||
};
|
||||
|
||||
@@ -145,13 +143,13 @@ SyncApi.prototype._registerStateListeners = function(room) {
|
||||
// we need to also re-emit room state and room member events, so hook it up
|
||||
// to the client now. We need to add a listener for RoomState.members in
|
||||
// order to hook them correctly. (TODO: find a better way?)
|
||||
reEmit(client, room.currentState, [
|
||||
client.reEmitter.reEmit(room.currentState, [
|
||||
"RoomState.events", "RoomState.members", "RoomState.newMember",
|
||||
]);
|
||||
room.currentState.on("RoomState.newMember", function(event, state, member) {
|
||||
member.user = client.getUser(member.userId);
|
||||
reEmit(
|
||||
client, member,
|
||||
client.reEmitter.reEmit(
|
||||
member,
|
||||
[
|
||||
"RoomMember.name", "RoomMember.typing", "RoomMember.powerLevel",
|
||||
"RoomMember.membership",
|
||||
@@ -1337,7 +1335,7 @@ SyncApi.prototype._onOnline = function() {
|
||||
|
||||
function createNewUser(client, userId) {
|
||||
const user = new User(userId);
|
||||
reEmit(client, user, [
|
||||
client.reEmitter.reEmit(user, [
|
||||
"User.avatarUrl", "User.displayName", "User.presence",
|
||||
"User.currentlyActive", "User.lastPresenceTs",
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user