用jQueryDeferred对象制作多个脚本加载器,我做得对吗

Making multiple scripts loader with jQuery Deferred object, am I doing it right?

本文关键字:加载 脚本 对象 jQueryDeferred      更新时间:2023-09-26

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

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

您可能会建议使用一些库,如requirejs或LABjs,但我认为我可以通过使用jQueryDeferred对象来创建类似的库。这是我迄今为止的尝试:

(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函数有什么问题吗?

问题是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返回一个promise,所以不需要将其封装在一个新的延迟对象中
  2. 使用promise对象可以获得更好的可读代码,因此$.require函数应该返回promise,而不是使用回调/errback

更改后的$.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);