-
-
Save tbranyen/3950130 to your computer and use it in GitHub Desktop.
/*! | |
* backbone.cacheit.js v0.1.0 | |
* Copyright 2012, Tim Branyen (@tbranyen) | |
* backbone.cacheit.js may be freely distributed under the MIT license. | |
*/ | |
(function(window) { | |
"use strict"; | |
// Localize global dependency references. | |
var Backbone = window.Backbone; | |
var $ = window.$; | |
var _ = window._; | |
// Patch the fetch method to retain a reference. | |
_.each(["Model", "Collection"], function(ctor) { | |
// Retain a copy of the original fetch method, since we are overidding it. | |
var oldFetch = Backbone[ctor].prototype.fetch; | |
// Override both Model and Collection `fetch` methods. | |
var newFetch = Backbone[ctor].prototype.fetch = function(options) { | |
options = options || {}; | |
// Save a reference to the original deferred. | |
var oldDef = this._def; | |
// Return early. | |
if (this._def && !options.reload && !this.reload) { | |
return this._def.promise(); | |
} | |
// If a deferred doesn't exist, create one. If the clear flag is provided, | |
// jump in to create a new deferred. | |
this._def = newFetch.deferred(); | |
// If the clear was provided and there is an existing deferred, resolve it | |
// once this has resolved. | |
if (options.reload && oldDef) { | |
this._def.done(oldDef.resolve); | |
} | |
// Call the original `fetch` method and store its return value (jqXHR). | |
var req = oldFetch.apply(this, arguments); | |
// Once the request has finished, resolve this deferred. | |
req.done(_.bind(function() { | |
this._def.resolveWith(this, [this]); | |
}, this)); | |
// Return the deferred to wait with. | |
return this._def.promise(); | |
}; | |
// Allow the jQuery dependency to be swapped out to use this in other | |
// enviornments. | |
Backbone[ctor].prototype.fetch.deferred = function() { | |
return $.Deferred(); | |
}; | |
}); | |
})(this); |
i'd say it's a great idea -- we use something similar where i work. a couple thoughts:
- you'll of course need a means of invalidating the cache (we use
{reload: true}
as a parameter) - you'll have to handle ajax calls that return out-of-order (i.e. you make the first fetch, then you call
fetch()
again and force a reload). this becomes a problem when you're making, say, a single page app and it gets big. there's different ways to handle the situation:- do you just never resolve the first deferred?
- do you resolve the first deferred with the results of the second? (this actually ends up being what our applications typically require)
Just added the option to clear with:
// Boolean to invalidate cache.
list.fetch(true);
Hrm. nevermind this isn't going to work like that, that'll break the ability to provide options. Definitely like your suggestion @aaronj1335
Cool updated it to do fetch({ reload: true })
instead of fetch(true)
Out of order will work correctly here, its fixed as a byproduct of how i wait until the latest fetch is done before calling resolved on the previous fetch()
;-)
@tbranyen looks awesome! for completeness 'old' should prolly be an array, and our experience is that you should unit test code like this til kingdom come b/c it's amazing how hard it is to catch all the corner cases.
do you know of any backbone libraries that provide this sort of caching out of the box (I'm thinking of stuff like supermodel here)?
I dunno, this was an idea that came out of #backbone-boilerplate on irc. Also I just made a project page: https://github.com/tbranyen/backbone.cacheit.js
url is now https://github.com/tbranyen/backbone.cacheit
Nice work Tim! Appreciate you documenting this well in the repo. Might include it in Aura as we've been thinking of doing something similar for the Backbone extension :)
Glad to see this. Many of my models only need to fetch, and they do that from a fairly static data set, so I've had to do something similar. Nice to see a concise solution we can all develop and share.
Great solution, Tim! Thanks.
I've done a bit of work with a CachingModel mixin which attempts to address HTTP ETag caching / 304 Not-Modified responses. At some point it would be amazing to integrate with this.
Very useful! Thanks! :)
Usage