Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve transport logic (support batch requests) #152

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 46 additions & 22 deletions src/transport.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@
*/

var Transport = (function() {
var pendingRequests = 0, maxParallelRequests, requestCache;
var pendingRequestsCount = 0,
pendingRequests = {},
maxPendingRequests,
requestCache;

function Transport(o) {
utils.bindAll(this);
Expand All @@ -15,8 +18,8 @@ var Transport = (function() {
requestCache = requestCache || new RequestCache();

// shared between all instances, last instance to set it wins
maxParallelRequests = utils.isNumber(o.maxParallelRequests) ?
o.maxParallelRequests : maxParallelRequests || 6;
maxPendingRequests = utils.isNumber(o.maxParallelRequests) ?
o.maxParallelRequests : maxPendingRequests || 6;

this.url = o.url;
this.wildcard = o.wildcard || '%QUERY';
Expand All @@ -37,6 +40,32 @@ var Transport = (function() {

utils.mixin(Transport.prototype, {

// private methods
// ---------------

_sendRequest: function(url) {
var that = this, jqXhr = pendingRequests[url];

if (!jqXhr) {
incrementPendingRequests();
jqXhr = pendingRequests[url] =
$.ajax(url, this.ajaxSettings).always(always);
}

return jqXhr;

function always() {
decrementPendingRequests();
pendingRequests[url] = null;

// ensures request is always made for the last query
if (that.onDeckRequestArgs) {
that.get.apply(that, that.onDeckRequestArgs);
that.onDeckRequestArgs = null;
}
}
},

// public methods
// --------------

Expand All @@ -50,36 +79,31 @@ var Transport = (function() {
this.replace(this.url, encodedQuery) :
this.url.replace(this.wildcard, encodedQuery);

// in-memory cache hit
if (resp = requestCache.get(url)) {
cb && cb(resp);
cb && cb(this.filter ? this.filter(resp) : resp);
}

// under the pending request threshold, so fire off a request
else if (belowPendingRequestsThreshold()) {
incrementPendingRequests();
$.ajax(url, this.ajaxSettings).done(done).always(always);
this._sendRequest(url).done(done);
}

// at the pending request threshold, so hang out in the on deck circle
else {
this.onDeckRequestArgs = [].slice.call(arguments, 0);
}

// success callback
function done(resp) {
resp = that.filter ? that.filter(resp) : resp;
var data = that.filter ? that.filter(resp) : resp;

cb && cb(resp);
requestCache.set(url, resp);
}
cb && cb(data);

// comlete callback
function always() {
decrementPendingRequests();

// ensures request is always made for the latest query
if (that.onDeckRequestArgs) {
that.get.apply(that, that.onDeckRequestArgs);
that.onDeckRequestArgs = null;
}
// cache the resp and not the result of applying filter
// in case multiple datasets use the same url and
// have different filters
requestCache.set(url, resp);
}
}
});
Expand All @@ -90,14 +114,14 @@ var Transport = (function() {
// --------------

function incrementPendingRequests() {
pendingRequests++;
pendingRequestsCount++;
}

function decrementPendingRequests() {
pendingRequests--;
pendingRequestsCount--;
}

function belowPendingRequestsThreshold() {
return pendingRequests < maxParallelRequests;
return pendingRequestsCount < maxPendingRequests;
}
})();
31 changes: 26 additions & 5 deletions test/transport_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ describe('Transport', function() {
describe('when request is available in cache', function() {
beforeEach(function() {
this.spy = jasmine.createSpy();
this.requestCache.get.andReturn(successData);
this.requestCache.get.andReturn({ data: ['val'] });

this.transport.filter = jasmine.createSpy().andReturn(['val']);
this.transport.get('query', this.spy);
this.request = mostRecentAjaxRequest();
});
Expand All @@ -55,8 +56,12 @@ describe('Transport', function() {
expect(this.request).toBeNull();
});

it('should invoke callback with response from cache', function() {
expect(this.spy).toHaveBeenCalledWith(successData);
it('should call filter', function() {
expect(this.transport.filter).toHaveBeenCalled();
});

it('should invoke callback with data', function() {
expect(this.spy).toHaveBeenCalledWith(['val']);
});
});

Expand All @@ -80,14 +85,30 @@ describe('Transport', function() {

expect(this.request.url).toEqual('http://example.com?q=$$has%20space');
});

it('should piggyback off of pending requests', function() {
this.spy1 = jasmine.createSpy();
this.spy2 = jasmine.createSpy();
this.transport2 = new Transport({ url: 'http://example.com?q=%QUERY' });

this.transport.get('hazel', this.spy1);
this.transport2.get('hazel', this.spy2);

this.request = mostRecentAjaxRequest();
this.request.response(successResp);

expect(ajaxRequests.length).toBe(1);
expect(this.spy1).toHaveBeenCalledWith(successData);
expect(this.spy2).toHaveBeenCalledWith(successData);
});
});

describe('when at concurrent request threshold', function() {
beforeEach(function() {
this.goodRequests = [];

for (var i = 0; i < 3; i++) {
this.transport.get('good');
this.transport.get('good' + i);
this.goodRequests.push(mostRecentAjaxRequest());
}

Expand Down Expand Up @@ -153,7 +174,7 @@ describe('Transport', function() {
var requests = [];

for (var i = 0; i < 3; i++) {
this.transport.get('good');
this.transport.get('good' + i);
requests.push(mostRecentAjaxRequest());
}

Expand Down