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

这是场景。 我正在进行$.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() on done() doSecond() is undefined错误。

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

我也对任何AMD loadersPromises答案Promises开放Promises

您还可以使用$.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执行此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(); } }); 

事实:

  • 你有first.js ,在这个脚本中是second.js的包含
  • 您需要调用在doSecond()中定义的doSecond()
  • 在调用之前,您需要确保doSecond()可用
  • 您无法直接更改first.jssecond.js但您可以让其他人更改它

可能的解决方案,从最好到最差排序

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

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

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

但是你提到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(); } 

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

3)请求将second.js脚本包含更改为包含在document.write

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

这将强制js在js可以继续之前解析document.write ,因此在你想要使用doSecond()时应该加载doSecond() 。 但这被认为是不好的做法,因为在解决document.write之前,没有其他任何事情可以发生。 因此,如果second.js需要永远加载或最终超时……这会导致用户体验不佳。 所以你应该避免这个选项,除非你因为“繁文缛节”的原因别无选择。

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() (在这个例子中,我以100ms的间隔尝试5次) ,或编码它只是永远检查永远(删除.attempts逻辑或交换它与setIntervalremoveInterval逻辑)。

您可以修改$.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而不是。 它使事件发生一次

您是否考虑过使用jQuery.when :

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

如果我是你,我会使用$ .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'); }); 
 $(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") }`