使用jQuery Deferred对象创建多个脚本加载器,我做得对吗?

时间:2014-04-11 16:32:18

标签: javascript jquery jquery-deferred

我正在开发一个更好的$.getScript函数版本,它可以:

  • 一次加载多个脚本
  • 缓存已加载的脚本
  • 在加载所有必需脚本后触发回调

您可能会建议一些库,例如requirejs或LABjs,但我想我可以通过使用jQuery Deferred对象来创建类似的库。这是我到目前为止的尝试:

(function($) {
    'use strict';

    var cachedScriptPromises = {};

    $.require = function(url, callback, errback) {
        var urls = (url instanceof Array) ? url : [url],
            promises = [];
        for (var i = 0; i < urls.length; i++) {
            var url = urls[i];
            if ( ! cachedScriptPromises[url]) {
                cachedScriptPromises[url] = $.Deferred(function(defer) {
                    $.ajax({
                        dataType: 'script',
                        cache: true,
                        url: url
                    }).then(defer.resolve, defer.reject);
                }).promise();
            }
            promises.push(cachedScriptPromises[url]);
        }
        $.when.apply($, promises).done(callback).fail(errback);
    };
})(jQuery);

这个想法是为每个脚本url存储一个promise对象,然后在解析所有延迟对象时触发回调。

我不知道如何正确测试这个功能但是请看一下这个测试页面,如果你多次刷新页面,可能会丢失其中一个输出(在Chrome上比在Firefox上更常出现) )。

更新 - 以下是我的测试用例之一,有时候没有记录任何内容:

$.require(['jquery.log.js', 'foo.js'], function() {
    $('#log').log('jquery.log.js and foo.js were loaded!');
});

jquery.log.js:

(function($) {
    $.fn.log = function(msg) {
        this.each(function() {
            $(this).append('<p>' + msg + '</p>');
        });
    };
})(jQuery);

foo.js:

$('#log').log('Foo!');

我的$.require功能有什么问题吗?

1 个答案:

答案 0 :(得分:0)

问题是jquery.log.js和foo.js会并行加载。如果首先加载foo.js,则会抛出错误(日志函数未定义)。所以我必须把$.require放在foo.js上:

$.require('jquery.log.js').done(function() {
    $('#log').log('Foo!');
});

关于我使用jQuery Deferred对象做的问题,以下是@Benjamin Gruenbaum建议的一些更改:

  1. $.ajax返回一个承诺,因此不需要将其包装在新的延迟对象中。
  2. 使用promise对象获得更好的可读代码,因此$.require函数应该返回一个promise,而不是使用callback / errback。
  3. 更改后

    $.require功能:

    (function($) {
        'use strict';
    
        var cachedScriptPromises = {};
    
        $.require = function(url) {
            var urls = (url instanceof Array) ? url : [url],
                promises = [];
            for (var i = 0; i < urls.length; i++) {
                var url = urls[i];
                if ( ! cachedScriptPromises[url]) {
                    cachedScriptPromises[url] = $.ajax({
                        dataType: 'script',
                        cache: true,
                        url: url
                    });
                }
                promises.push(cachedScriptPromises[url]);
            }
            return $.when.apply($, promises);
        };
    })(jQuery);