【问题标题】:In jQuery, how to deal with parallel script loading?在 jQuery 中,如何处理并行脚本加载?
【发布时间】:2021-09-30 11:43:44
【问题描述】:

我在应用程序开发中遇到了这个问题,不知道该尝试什么。我有以下功能:

// Function to load Scripts on the fly
$.loadScript = function(url, arg1, arg2) {
    var cache = false,
        callback = null;
    //arg1 and arg2 can be interchangable
    if ($.isFunction(arg1)) {
        callback = arg1;
        cache = arg2 || cache;
    } else {
        cache = arg1 || cache;
        callback = arg2 || callback;
    }
    
    var that = this;
    var load = true;

    var deferred = jQuery.Deferred();
    if (jQuery.isFunction(callback)) {
        deferred.done(function(){
            callback.call(that);
        });
    };

    if( url.constructor === Array ){
        function loadScript(i) {
            if (i < url.length) {
                var el = url[i];

                //check all existing script tags in the page for the url
                if (window.loadedScripts.indexOf(el) === -1) {
                    window.loadedScripts.push(el);
                    //didn't find it in the page, so load it
                    $.ajax({
                        url: el,
                        success: function(e){
                            __info('Loaded script. url: '+el, 'verbose');
                            loadScript(i + 1);
                        },
                        complete: function(e){
                            if(typeof e.done !== 'function'){
                                $(function () {
                                    $('<script>')
                                        .attr('type', 'text/javascript')
                                        .text(e.responseText)
                                        .prop('defer',true)
                                        .appendTo('head');
                                })
                            }
                        },
                        dataType: 'script',
                        cache: cache
                    });
                }
                //-----------------
            } else {
                deferred.resolve();
            }
        }
        loadScript(0);

        return deferred;
    } else {
        //check all existing script tags in the page for the url
        if (window.loadedScripts.indexOf(url) === -1) {
            window.loadedScripts.push(url);
            //didn't find it in the page, so load it
            $.ajax({
                url: url,
                success: function(e){
                    __info('Loaded script. url: '+url, 'verbose');
                    deferred.resolve();
                },
                complete: function(e){
                    if(typeof e.done !== 'function'){
                        $(function () {
                            $('<script>')
                                .attr('type', 'text/javascript')
                                .text(e.responseText)
                                .prop('defer',true)
                                .appendTo('head');
                        })
                    }
                },
                dataType: 'script',
                cache: cache
            });
        } else {
            deferred.resolve();
        };
    }
};

这个函数的作用类似于$.getScript,但加载了几个脚本(或只加载一个),最后触发一个回调并引入缓存参数来处理这个应用程序的自定义缓存。

它已经可以正常工作了,除非在多个脚本请求并行加载的情况下。当这种情况发生时,第二组脚本进入函数,观察第一个脚本(块通用)正在加载并且不加载它(但需要等待它)。但是,在这种情况下,在第一个块中,第一个脚本尚未加载,而在第二个块中,此脚本是必需的,但被跳过了。

使用示例:

<script type="text/javascript">
    $.loadScript([
        "//code.highcharts.com/highcharts.src.js",
        "//code.highcharts.com/modules/data.js",
        "//code.highcharts.com/modules/treemap.js"
    ], true, function(){ 
        $.ajax({
             type: 'POST',
             url: '/skip-process/charts/graph1',
             success: function(data){
                 console.log($.parseJSON(data));
                 var chart = new Highcharts.Chart($.parseJSON(data));
             }
        });
    });
</script>

如何处理多个脚本请求同时加载同一个脚本而他们没有等待呢?非常感谢。

【问题讨论】:

  • 看起来非常复杂。为什么需要使用 $.ajax 获取脚本内容以作为文本插入到脚本元素中,而不仅仅是将 url 传递给脚本元素的 src
  • 因为在app中,脚本是ajax根据事件按需加载的,并行但会异步,可能会被缓存,需要单独或者多个同时回调结尾。一些脚本是按需生成的。

标签: javascript jquery jquery-deferred


【解决方案1】:

我会使用$.getScript 并使用它返回的承诺并使用then() 来解决这个问题,而不是将回调传递给函数。

以下没有经过很好的测试,但应该非常接近

$.loadScript = function (url) {
  // create loadedScripts array if doesn't already exist
  const cache = window.loadedScripts = window.loadedScripts || [];
  // always use array even when string passed in, reduces duplicate code
  const normalizeUrl = (url) => (Array.isArray(url) ? url : [url]);

  let urlArr = normalizeUrl(url).filter((u) => {
    if (cache.includes(u)) {
      return false;
    }
    cache.push(u);
    return true;
  });

  // if all cached just return a resolved promise
  if (!urlArr.length) {
    return Promise.resolve();
  }
  
  // create initial getScript promise
  const reqs = $.getScript(urlArr.shift());

  while (urlArr.length) {
    const u = urlArr.shift();
    // chain a new then() for each url
    reqs.then(() => $.getScript(u));
  }
  //return getScript promise chain
  return reqs;
};

$.loadScript([
    'remote-script-1.js', 
    'remote-script-2.js'])
 .then(() => {
  console.log('All Loaded');    
});

【讨论】:

  • 首先,谢谢。查看您的代码后,我认为它有同样的问题。如果我有以下代码,它会尝试同时加载脚本 1,并且第二个块不会等待第一个完成脚本 1 的加载。 $.loadScript([ 'remote-script-1.js', 'remote-script-2.js']) .then(() =&gt; { console.log('All Loaded'); }); $.loadScript([ 'remote-script-1.js', 'remote-script-3.js']) .then(() =&gt; { console.log('All Loaded'); });
  • 正确。所有这些都是异步的。您需要在第一个的 then() 中调用第二个。没有办法让这一切同步
  • 有问题。每个block都是从不同的AJAX函数结果调用的,代码执行时无法控制。只有加载脚本的函数内部发生了什么。
猜你喜欢
  • 1970-01-01
  • 2013-07-30
  • 2011-06-15
  • 2012-11-26
  • 1970-01-01
  • 1970-01-01
  • 2011-11-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多