Skip to content

Commit

Permalink
Remove TimeoutContext, Debounce and inline relevant calls
Browse files Browse the repository at this point in the history
  • Loading branch information
pik committed Jan 20, 2017
1 parent 2833021 commit 900b68c
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 276 deletions.
2 changes: 1 addition & 1 deletion spec/integ/matrix-client-syncing.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe("MatrixClient syncing", function() {
};

it("should start keepalives if a /sync hits a timeout error", function(done) {
httpBackend.when("GET", "/sync").respond(200, syncData).waitFor(61*1000);
httpBackend.when("GET", "/sync").respond(200, syncData).waitFor((30+80+1)*1000);
client.startClient();
spyOn(client._syncApi, '_startKeepAlives').andCallThrough();
// Work-around since
Expand Down
57 changes: 0 additions & 57 deletions spec/unit/http-api.spec.js

This file was deleted.

85 changes: 0 additions & 85 deletions spec/unit/realtime-callbacks.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,91 +26,6 @@ describe("realtime-callbacks", function() {
callbacks.setNow();
});

describe("debounce", function() {
it(`should call the callback after the timeout
if it has not been debounced`, function() {
let callback = jasmine.createSpy();
let jobName = callbacks.debounce(undefined, callback, 100);
expect(callback).not.toHaveBeenCalled();
tick(100);
expect(callback).toHaveBeenCalled();
expect(callbacks.__getDebouncers()[jobName]).toBe(undefined);
});

it(`should return a randomly generated jobName to
reference the debouncer if one is not provided`, function() {
let callback = jasmine.createSpy();
let jobName = callbacks.debounce(undefined, callback, 100);
expect(callback).not.toHaveBeenCalled();
expect(typeof(jobName)).toBe("string");
expect(callbacks.__getDebouncers()[jobName]).toBeTruthy();
});

it(`should accept a jobName as an argument
to reference the debouncer`, function() {
let callback = jasmine.createSpy();
let jobName = callbacks.debounce("foo", callback, 100);
expect(callback).not.toHaveBeenCalled();
expect(jobName).toEqual("foo");
expect(callbacks.__getDebouncers()[jobName]).toBeTruthy();
});

it("should run waitMs after the last debounce call", function() {
let callback = jasmine.createSpy();
let waitMs = 100;
let jobName = callbacks.debounce(undefined, callback, waitMs);
expect(callback).not.toHaveBeenCalled();
tick(90);
expect(callback).not.toHaveBeenCalled();
callbacks.debounce(jobName);
tick(10);
expect(callback).not.toHaveBeenCalled();
callbacks.debounce(jobName);
tick(90);
expect(callback).not.toHaveBeenCalled();
tick(10);
expect(callback).toHaveBeenCalled();
});

it("will use new waitMs if one is provided", function() {
let callback = jasmine.createSpy();
let waitMs = 100;
let jobName = callbacks.debounce(undefined, callback, waitMs);
expect(callback).not.toHaveBeenCalled();
tick(90);
expect(callback).not.toHaveBeenCalled();
callbacks.debounce(jobName, undefined, 0);
tick(0);
expect(callback).toHaveBeenCalled();
});

it("will use new callback if one is provided", function() {
let callback = jasmine.createSpy();
let newCallback = jasmine.createSpy();
let waitMs = 100;
let jobName = callbacks.debounce(undefined, callback, waitMs);
expect(callback).not.toHaveBeenCalled();
tick(90);
expect(callback).not.toHaveBeenCalled();
callbacks.debounce(jobName, newCallback);
tick(100);
expect(callback).not.toHaveBeenCalled();
expect(newCallback).toHaveBeenCalled();
});
});

describe("clearDebounce", function() {
it("clears a debouncer by jobName", function() {
let jobName = callbacks.debounce(undefined, function() {}, 1000);
expect(callbacks.clearDebounce(jobName)).toEqual(true);
expect(callbacks.__getDebouncers()[jobName]).toBe(undefined);
});

it("returns false if no debouncer was cleared", function() {
expect(callbacks.clearDebounce("fooBar")).toEqual(false);
});
});

describe("setTimeout", function() {
it("should call the callback after the timeout", function() {
const callback = jasmine.createSpy();
Expand Down
68 changes: 20 additions & 48 deletions src/http-api.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,44 +83,6 @@ module.exports.MatrixHttpApi = function MatrixHttpApi(event_emitter, opts) {
this.uploads = [];
};

/**
* Construct a Timeout Context.
* Set onProgressCheck to receive progressEvent from an XMLHTTPRequest.
* TimeoutContext will debounce the timeoutFunction for waitMs if progress
* is being made.
*
* @constructor
* @param {Function} timeoutFunction The function to debounce with waitMs.
* @param {Number} waitMs The time to debounce the callback in miliseconds.
*
*/
const TimeoutContext = function TimeoutContext(timeoutFunction, waitMs) {
this.lastLoaded = null;
this.jobName = null;
this.timeoutFunction = timeoutFunction;
this.waitMs = waitMs;
};

// For testing
module.exports.__TimeoutContext = TimeoutContext;

TimeoutContext.prototype.onProgressCheck = function(progressEvent) {
if (this.lastLoaded === null || progressEvent.loaded > this.lastLoaded) {
callbacks.debounce(this.jobName);
this.lastLoaded = progressEvent.loaded;
}
return progressEvent;
};

TimeoutContext.prototype.stop = function() {
callbacks.clearDebounce(this.jobName);
};

TimeoutContext.prototype.start = function() {
this.jobName = callbacks.debounce(undefined, this.timeoutFunction, this.waitMs);
};


module.exports.MatrixHttpApi.prototype = {

/**
Expand Down Expand Up @@ -667,22 +629,33 @@ module.exports.MatrixHttpApi.prototype = {

let timedOut = false;
let req;
let timeoutContext;
let loaded;
let timeoutId;
let onProgressFunc;
const localTimeoutMs = opts.localTimeoutMs || this.opts.localTimeoutMs;

if (localTimeoutMs) {
var timeoutFunction = function() {
let timedOut = function () {
timedOut = true;
if (req && req.abort) {
req.abort();
}
defer.reject(new module.exports.MatrixError({
error: "Locally timed out waiting for a response",
errcode: "ORG.MATRIX.JSSDK_TIMEOUT",
timeout: localTimeoutMs,
timeout: localTimeoutMs
}));
};
timeoutContext = new TimeoutContext(timeoutFunction, localTimeoutMs);

timeoutId = setTimeout(timedOut, localTimeoutMs);

onProgressFunc = function(ev) {
if (loaded === null || ev.loaded > loaded) {
loaded = ev.loaded;
clearTimeout(timeoutId);
timeoutId = setTimeout(timedOut, localTimeoutMs);
}
};
}

const reqPromise = defer.promise;
Expand All @@ -701,8 +674,8 @@ module.exports.MatrixHttpApi.prototype = {
_matrix_opts: this.opts,
},
function(err, response, body) {
if (timeoutContext) {
timeoutContext.stop();
if (localTimeoutMs) {
clearTimeout(timeoutId);
if (timedOut) {
return; // already rejected promise
}
Expand All @@ -719,15 +692,14 @@ module.exports.MatrixHttpApi.prototype = {
handlerFn(err, response, body);
}
);
if (timeoutContext) {
req.onprogress = timeoutContext.onProgressCheck.bind(this);
timeoutContext.start();
}
if (req && req.abort) {
// FIXME: This is EVIL, but I can't think of a better way to expose
// abort() operations on underlying HTTP requests :(
reqPromise.abort = req.abort.bind(req);
}
if (localTimeoutMs) {
req.onprogress = onProgressFunc;
}
} catch (ex) {
defer.reject(ex);
if (callback) {
Expand Down
85 changes: 0 additions & 85 deletions src/realtime-callbacks.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,91 +56,6 @@ module.exports.setNow = function(f) {
};
let _now = Date.now;

// Internal hash of active debouncers
const _debouncers = {};

class Debouncer {

constructor(jobName) {
this.jobName = jobName;
}

complete() {
delete _debouncers[this.jobName];
this.func();
}

start(func, waitMs) {
if (func) this.func = func;
if (waitMs > 0) this.waitMs = waitMs;
this.timeoutId = exports.setTimeout(this.complete.bind(this), this.waitMs);
}

stop() {
if (this.timeoutId !== null || this.timeoutId !== undefined) {
exports.clearTimeout(this.timeoutId);
delete this.timeoutId;
}
}
}

/**
* Implementation of debounce a which will call the callback after
* a waitMs period if it is not debounced within that period.
*
* After a jobName has been defined it may be called without
* {func} or {waitMs} arguments to debounce with initial settings.
* New {func} or {waitMs} params will over-write ones previously
* set in the debouncer.
* If called without a jobName a random one is generated.
*
* @param {String} jobName a jobName to reference the debouncer
* @param {function} func callback to be called after a delay
* @param {Number} waitMs number of milliseconds to delay by
*
* @return {String} jobName a jobName to reference the debouncer
*/

module.exports.debounce = function(jobName, func, waitMs) {
if (jobName === undefined || jobName === null) {
// Make sure a randomly generated jobName does not already exist
while(_debouncers[jobName = Math.random().toString(16).slice(2, 15)]);
}
let debouncer = _debouncers[jobName];
if (debouncer) {
debouncer.stop();
} else {
_debouncers[jobName] = debouncer = new Debouncer(jobName, func, waitMs);
}
debouncer.start(func, waitMs);
return jobName;
};

/**
* Clear a debouncer by jobName.
*
* @param {String} a jobName to reference the debouncer.
*
* @return {Boolean} whether a debouncer with jobName was cleared.
*/

module.exports.clearDebounce = function(jobName) {
if (_debouncers[jobName]) {
_debouncers[jobName].stop();
return delete _debouncers[jobName];
} else {
return false;
}
};

/**
* -- Debug only --
* @return {Object} debouncer Returns the hash of existing debouncers.
*/
module.exports.__getDebouncers = function() {
return _debouncers;
};

/**
* reimplementation of window.setTimeout, which will call the callback if
* the wallclock time goes past the deadline.
Expand Down

0 comments on commit 900b68c

Please sign in to comment.