{"id":1836,"date":"2013-02-13T07:45:00","date_gmt":"2013-02-13T13:45:00","guid":{"rendered":"http:\/\/www.wiredprairie.us\/blog\/?p=1836"},"modified":"2013-02-13T07:18:26","modified_gmt":"2013-02-13T13:18:26","slug":"smarter-queuing-of-files-using-jquery-deferred-and-when","status":"publish","type":"post","link":"https:\/\/www.wiredprairie.us\/blog\/index.php\/archives\/1836","title":{"rendered":"Smarter queuing of files using jQuery deferred and when"},"content":{"rendered":"

I had a somewhat simple need \u2026a single web page style application using Backbone.JS<\/a> had multiple template files it needed to download, if and only if the files hadn\u2019t already been downloaded before. <\/p>\n

While I suppose I could have relied on browser caching, I wanted to manage the requests in JavaScript. The trick was that at any given time, a request might include one or more template files, and some of the request might have already been made, or in progress.<\/p>\n

Here\u2019s the queuing function:<\/p>\n

(function<\/span>() {\n    var<\/span> allFiles = {};\n    var<\/span> allResults = {};\n\n    var<\/span> queueRequest = function<\/span> (path, files, options) {\n        options = options || {};\n        files = $.isArray(files) ? files : [files];\n        \n        \/\/ this will contain all of the deferreds that will be <\/span>\n        \/\/ wrapped with a `when` deferred<\/span>\n        var<\/span> when = [];\n        \/\/ might want to override some defaults...<\/span>\n        var<\/span> ajaxOpt = $.extend({\n            dataType: 'json'<\/span>,\n            type: 'GET'<\/span>\n        }, options.ajax || {});\n\n        \/\/ go thru each file<\/span>\n        $.each(files, function<\/span> (i, file) {\n            var<\/span> xhr;\n            \/\/ if we've not seen it before<\/span>\n            if<\/span> (typeof<\/span> allFiles[file] === 'undefined'<\/span>) {\n                \/\/ kick it off<\/span>\n                xhr = $.ajax(path + file, ajaxOpt).done(function<\/span> (data) {\n                    \/\/ store the file results in the hash index<\/span>\n                    allResults[file] = data;\n                });\n                \/\/ keep the deferred for later use<\/span>\n                allFiles[file] = xhr;\n                \/\/ and keey this for later<\/span>\n                when.push(xhr);\n            } else<\/span> {\n                \/\/ already seen this, so just pack it on<\/span>\n                when.push(allFiles[file]);\n            }\n        });\n        \/\/ return all of the built up deferreds as a <\/span>\n        \/\/ single when. this will then wait until <\/span>\n        \/\/ all are satisfied<\/span>\n        return<\/span> $.when.apply(this<\/span>, when).done(function<\/span> () {\n            if<\/span> ($.isFunction(options.done)) {\n                options.done.call(this<\/span>, allResults);\n            }\n        }).fail(options.error);\n    };\n\n    window.Queue = queueRequest;\n})();<\/pre>\n

Queuing is easy enough:<\/p>\n

Queue(app_url("api\/template\/"<\/span>), templates, {\n    done: function<\/span> (allFiles) {\n        _.each(templates, function<\/span> (n) {\n            \/\/ compile only if needed<\/span>\n            if<\/span> (typeof<\/span> allTemplates[n] === 'undefined'<\/span>) {\n                allTemplates[n] = _.template(allFiles[n]);\n            }\n        });\n        if<\/span> (typeof<\/span> callback === 'function'<\/span>) {\n            callback.call(context, allTemplates);\n        }\n    }\n});<\/pre>\n

allTemplates<\/strong> is a list of compiled underscore templates stored elsewhere.<\/p>\n