Warning: Code Examples are in CoffeeScript
I have been working on a javascript application over the past couple of months built with the Spine.js framework and things have gone really well. We are holding onto data for a current session and some of the less volatile data is only fetched once during that session and only when it's needed.
For example, we have "Discussions" and "Users" and we only fetch these models once during a session and in the case of a Discussion, we only fetch a full discussion one at a time. Also, while we batch fetch Users, we cannot guarantee when we render a Discussion along with Users and their avatars that all of the users are present at rendering time. This caused quite a bit of code that looks like this...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
render: (discussion) -> | |
# render stuff | |
change: (discussion_id) -> | |
@discussion_id = discussion_id | |
# if the discussion has already been retrieved from the server (exists is local storage) | |
# then directly render it | |
if Discussion.exists(discussion_id) | |
@render(Discussion.find(discussion_id)) | |
else | |
# else bind to the refresh event, and just fetch that single discussion | |
Discussion.bind('refresh', @_onDiscussionRefresh) | |
Discussion.fetch(id: discussion_id) | |
_onDiscussionRefresh: => | |
# when invoked, double check to ensure that the item you expect to be present | |
# otherwise wait until it comes back. | |
if Discussion.exists(@discussion_id) | |
Discussion.unbind('refresh', @_render) | |
@render(Discussion.find(@discussion_id)) |
Yeah that sucks having code that looks like that littered all across the application and it can get even worse if you need to ensure that multiple pieces of data already exist...so tonight, I cleaned it up with a promise and am really happy with the results
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{flatten} = require('lib/util_functions') | |
promise = (model, ids...) -> | |
ids = flatten(ids) | |
promises = ids.map (id) -> | |
ModelPromise.getPromise(model, id) | |
return $.when.apply($, promises) | |
class ModelPromise | |
@getPromise: (model, id) -> | |
(new @(model, id)).promise() | |
constructor: (model, id) -> | |
@model = model | |
@id = id | |
promise: -> | |
@_promise ||= @deferred().promise() | |
deferred: -> | |
@_deferred ||= @_buildDeferred() | |
_buildDeferred: -> | |
deferred = $.Deferred() | |
if @model.exists(@id) | |
deferred.resolve() | |
else | |
@model.bind('refresh', @_resolve) | |
@model.fetch(id: @id) | |
return deferred | |
_resolve: => | |
if @model.exists(@id) | |
@deferred().resolve() | |
exports.promise = promise | |
exports.ModelPromise = ModelPromise |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{promise} = require('model_promise') | |
render: (discussion) -> | |
# render stuff | |
change: (discussion_id) -> | |
@discussion_id = discussion_id | |
$.when(promise(Discussion, discussion_id)).then => | |
@render(Discussion.find(discussion_id)) | |
# Multiple models needed - no worries | |
change: (discussion_id, user_id1, user_id2) -> | |
$.when(promise(Discussion, discussion_id), promise(User, user_id1, user_id2)).then => | |
# whatever you need, all models are ensured to be present |
Additional Reading/Resources:
- Promises/A - http://wiki.commonjs.org/wiki/Promises/A
- jQuery Deferred - http://api.jquery.com/category/deferred-object/
No comments:
Post a Comment