如何查找链接的异步脚本是否已加载?

时间:2014-03-22 02:43:40

标签: javascript jquery amd

这是场景。我正在进行$.getScript()函数调用以获取我的javascript文件中的脚本。我从$.getScript()下载的脚本会尝试下载其依赖的其他脚本。在我的脚本中,我使用done()检查脚本是否完全加载。如果确实如此,那么我尝试调用我刚刚加载的脚本$.getScript,而不是加载到其中的脚本。

它让人感到困惑所以让我演示一些代码: -

//my script.js
$.getScript("http://myexternaljs.com/first.js").done(function(){
    doSecond(); //<- this resides in second.js which is being called in first.js below
}

//first.js
(function(){
    $.getScript("http://firstexternal.com/second.js");
}());

//second.js
function doSecond(){
    return console.log("hello world");
}

此处的问题是second.js需要一点时间下载,因此我在doSecond() is undefined上对doSecond()的调用时出现done()错误。

我可以使用超时并检查是否加载second.js但是有更好的方法吗?

我也可以查看任何AMD loadersPromises个答案。

6 个答案:

答案 0 :(得分:4)

您还可以使用$.ajaxSuccess

$(document).ajaxComplete(function(ev, jqXhr, options) {
  // You could be as specific as you need to here.
  if ( options.url.match('second.js') ) {
    doSecond();
  }
});

或者,您可以在ajaxComplete

中执行此操作
$(document).ajaxComplete(function(ev, jqXhr, options) {
  // You could simplify to 
  // doSecond && doSecond()
  // if you can trust that it will always be a function
  if ( doSecond && $.isFunction(doSecond) ) {
    doSecond();
  }
});

答案 1 :(得分:2)

事实:

  • 您有first.js,此脚本中包含second.js
  • 您需要拨打doSecond()
  • 中定义的second.js
  • 在致电之前,您需要确保doSecond()可用
  • 您无法直接更改first.jssecond.js但您可以让其他人更改

可能的解决方案,按最佳状态排序

1)请求second.js移除first.js。单独调用它们以便嵌套它们:

$.getScript("first.js").done(function(){
  $.getScript("second.js").done(function(){
    doSecond(); 
  });
});

这是最好的解决方案。有其他替代方案,原则上基本上做同样的事情(例如其他人的答案)。如果first.js在继续之前同步包含second.js或以其他方式强行加载(例如下面的选项#3),那么您就不会开始遇到此问题。因此,first.js必须构建为处理second.js be * a *同步加载,因此将它们从文件中删除并不应该成为问题你自己称呼它。

但你提到second.js的位置是在first.js中定义的,所以这对你来说是不可行的(为什么不呢?他们可以将路径/ /脚本放在变量中供你访问?)

2)请求second.js包含在.done或等效加载的回调中,该回调会弹出您可以定义的回调函数。

// inside first.js
$.getScript("second.js").done(function(){
  if (typeof 'secondLoaded'=='function') 
    secondLoaded();      
});


// on-page or elsewhere, you define the callback
function secondLoaded() {
  doSecond();
}

这只是一个通用且简单的回调&#34;例。有一百万种方法可以实现这一原则,具体取决于这些脚本中实际存在的内容以及人们愿意为重构事物做出多少努力。

3)请求second.js脚本包含更改,以便通过document.write

包含在内
document.write(unescape("%3Cscript src='second.js' type='text/javascript'%3E%3C/script%3E"));

这会强制js在js继续之前解析document.write,因此在您要使用second.js时应加载doSecond()。但这被认为是不良做法,因为在解决document.write之前,没有其他任何事情可以发生。因此,如果second.js永远需要加载或最终超时......这会导致不良用户体验。所以你应该避免这个选项,除非你没有别的选择,因为&#34; red tape&#34;原因。

4)使用setTimeout尝试等待加载。

function secondLoaded() {
  if (!secondLoaded.attempts) secondLoaded.attempts = 0;
  if (secondLoaded.attempts < 5) {
    if (typeof 'doSecond'=='function') {
      doSecond();
    } else {
      secondLoaded.attempts++;
      window.setTimeout('secondLoaded()',100);
    }
  }
}
secondLoaded();

我列出的这比#3更糟糕,但实际上它是一种折腾..在这种情况下,你基本上要么在决定截止时间之间选择而不执行doSecond()(在这个例子中) ,我以100毫秒的间隔尝试5次),或者将其编码为永远永远检查(删除.attempts逻辑,或者用setIntervalremoveInterval逻辑交换它。

答案 2 :(得分:1)

您可以修改$.getScript的工作方式。

$.fn.getScript = (function() {
  var originalLoad = $.fn.getScript;
  return function() {
    originalLoad.apply($, arguments);
    $(document).trigger('load_script')
  };
})();

每次加载脚本时都会触发一个事件。

因此,您可以等待这些事件触发并检查您的方法是否存在。

$(document).one('second_loaded', function() {
  doSecond();
}).on('load_script', function() {
  doSecond && document.trigger('second_loaded');
});

请注意one而不是on。它会使事件触发一次

答案 3 :(得分:0)

您是否考虑过使用jQuery.when

$.when($.getScript("http://myexternaljs.com/first.js"),$.getScript("http://firstexternal.com/second.js"))
.done(function(){
  doSecond();
}

答案 4 :(得分:0)

如果我是你,我会立即使用$ .getScript并执行上述技巧的组合。阅读完评论之后,似乎有可能两次加载相同的脚本。

如果你使用requirejs,这个问题就解决了,因为它只加载每个脚本一次。这里的答案是继续提出要求。

装载机:

var requiredScripts = {};

function importScript(url) {
   if (!requiredScripts[url]) {
       requiredScripts[url] = $.getScript(url);
   }

   return requiredScripts[url];
}

用法:

// should cause 2 requests
$.when(
    importScript('first.js'),
    importScript('second.js')
).done(function() {
    // should cause no requests
    $.when(importScript('first.js')).done(function() {});
    $.when(importScript('second.js')).done(function() {});
});

使用MomentJS和UnderscoreJS的真实世界示例http://jsfiddle.net/n3Mt5/

当然,requirejs会为你处理这个并且语法更好。

Requirejs

define(function(require) {
    var first = require('first'),
        second = require('second');
});

答案 5 :(得分:0)

$(window).one("second", function(e, t) {
  if ( $(this).get(0).hasOwnProperty(e.type) && (typeof second === "function") ) { 
   second(); console.log(e.type, e.timeStamp - t); 
   $(this).off("second") 
  };
   return !second
});


$.getScript("first.js")
.done(function( data, textStatus, jqxhr ) {
  if ( textStatus === "success" ) {
    first();
    $.getScript("second.js") 
    .done(function( script, textStatus, jqxhr, callbacks ) { 
      var callbacks = $.Callbacks("once");
      callbacks.add($(window).trigger("second", [ $.now() ]));         
      return ( textStatus === "success" && !!second 
             ? callbacks.fire()
             : $(":root").animate({top:"0"}, 1000, function() { callbacks.fire() })
             )
    });
  }; 
})
// `first.js` : `function first() { console.log("first complete") }`
// `second.js` : `function second() { console.log("second complete") }`