通过$ .getScript完成所有脚本加载后调用处理程序

时间:2022-12-06 09:39:38

So let's say we load a bunch of scripts via $.getScript:

所以我们假设我们通过$ .getScript加载一堆脚本:

$.getScript( 'js/script1.js' );
$.getScript( 'js/script2.js' );
$.getScript( 'js/script3.js' );

Now, I'd like to invoke a handler when all those scripts finished loading. I tried binding a handler for the global ajaxStop event. According to the docs, the ajaxStop global event is triggered if there are no more Ajax requests being processed.

现在,我想在所有这些脚本完成加载时调用一个处理程序。我尝试绑定全局ajaxStop事件的处理程序。根据文档,如果不再处理Ajax请求,则会触发ajaxStop全局事件。

$( document ).ajaxStop( handler );

but it doesn't work (the handler is not invoked).

但它不起作用(不调用处理程序)。

Live demo: http://jsfiddle.net/etGPc/2/

现场演示:http://jsfiddle.net/etGPc/2/

How can I achieve this?

我怎样才能做到这一点?

3 个解决方案

#1


2  

you're doing it wrong anyways :)

你反正做错了:)

here is one of the things that can happen: imagine the scripts are cached, then they might be loaded in no time. so, straight after the first call $.getScript( 'js/script1.js' ); the script will be available and $.ajaxStop (might!!!) get called, in the worst case that would happen three times.

这是可能发生的事情之一:想象一下脚本是否被缓存,然后它们可能会立即被加载。所以,直接在第一次调用$ .getScript('js / script1.js')之后;脚本将可用并且$ .ajaxStop(可能!!!)被调用,在最坏的情况下会发生三次。

to answer your question indirectly i would propose a different solution which avoids this race condition alltogether. you can try it here: http://jsfiddle.net/etGPc/8/

为了间接回答你的问题,我会提出一个完全避免这种竞争条件的不同解决方案。你可以在这里试试:http://jsfiddle.net/etGPc/8/

var urls, log, loaded;

// urls to load
urls = [
    'https://raw.github.com/h5bp/html5-boilerplate/master/js/script.js',
    'https://raw.github.com/h5bp/html5-boilerplate/master/js/plugins.js',
    'https://raw.github.com/h5bp/html5-boilerplate/master/js/libs/modernizr-2.0.6.min.js'
];

// urls loaded 
loaded = []; 


log = $( '#log' ); 

$.map( urls, function( url ){
    $.getScript( url, function(){
        // append to loaded urls
        loaded.push( url ); 
        log.append( "loaded " + url + "<br>" ); 

        // all loaded now? 
        if( loaded.length == urls.length ){
            log.append( "<b>all done!</b>" ); 
        }
    } ); 
} ); 

if you haven't seen jQuery.map before: it's not really different from a for-loop :)

如果您之前没有见过jQuery.map:它与for循环没有什么不同:)

another advantage here is that this method doesn't get confused if you have other ajax requests going on at the same time.

这里的另一个优点是,如果您同时进行其他ajax请求,则此方法不会混淆。

p.s. to avoid naming-*es you can wrap the entire thing in a self-executing function, i.e.

附:为了避免命名冲突,你可以将整个事物包装在一个自动执行的函数中,即

function(){
    var urls, log, loaded; 

    ... all code here ... 
} (); 

Update: Refactored the code a bit...

更新:重新编写代码...

var urls, loadedUrls, log;

urls = [
    'https://raw.github.com/h5bp/html5-boilerplate/master/js/script.js',
    'https://raw.github.com/h5bp/html5-boilerplate/master/js/plugins.js',
    'https://raw.github.com/h5bp/html5-boilerplate/master/js/libs/modernizr-2.0.6.min.js'
];    

loadedUrls = [];

log = $( '#log' )[0];

urls.forEach(function ( url ) {
    $.getScript( url, function () {
        loadedUrls.push( url );

        $( log ).append( 'loaded ' + url + '<br>' );

        if( loadedUrls.length === urls.length ){
            $( log ).append( '<b>all done!</b>' );
        }
    });
});

Live demo: http://jsfiddle.net/etGPc/10/

现场演示:http://jsfiddle.net/etGPc/10/

#2


3  

I digged a little more into this issue and it's indeed the cross-domain script request that's the caveat. As I posted in the comments, that scenario has been implemented such that it sets the global option to false. This makes jQuery not to fire global ajax events. (No idea why that has been implemented though.)

我在这个问题上多挖了一点,这确实是跨域脚本请求,这是一个警告。正如我在评论中发布的那样,该场景已经实现,因此它将全局选项设置为false。这使得jQuery不会触发全局ajax事件。 (不知道为什么会这样实现。)

This can be confirmed with this fiddle (pass means ajaxStop is fired):

这可以通过这个小提琴来确认(传递意味着ajaxStop被触发):

  • cross-domain, no script: pass
  • 跨域,没有脚本:通过

  • cross domain, script: fail
  • 跨域,脚本:失败

  • no cross-domain, no script: pass
  • 没有跨域,没有脚本:通过

  • no cross-domain, script: pass
  • 没有跨域,脚本:通过

The most straight-forward thing to do is simply adding another prefilter which forces the global option to true:

最直接的做法是简单地添加另一个prefilter,强制全局选项为true:

jQuery.ajaxPrefilter( "script", function() {
    s.global = true;
});

This also makes this failing scenario pass in the fiddle.

这也使得这种失败的场景在小提琴中传递。

#3


2  

While dealing with my own problem I found a much better and elegant solution to this problem, and it is using the jQuery deffered object. I think you already know about the function, maybe for another usecase, but it works great for loading files, and fire functions when eveything is done. Code is as follows:

在处理我自己的问题时,我发现了一个更好,更优雅的解决方案,它正在使用jQuery deffered对象。我想你已经知道了这个函数,可能是另一个用例,但它非常适合加载文件,并在完成eveything时触发函数。代码如下:

function getLatestNews() {
    return $.get('/echo/js/?delay=2&js=', function(data) {
        console.log('news data received');
        $('.news').css({'color':'blue'});
    });
}

function getLatestReactions() {
    return $.get('/echo/js/?delay=5&js=', function(data) {
        console.log('reactions data received');
        $('.reactions').css({'color':'green'});
    });
}

function prepareInterface() {
    return $.Deferred(function(dfd) {
        var latest = $('.news, .reactions');
        latest.slideDown(500, dfd.resolve);
        latest.addClass('active');
    }).promise();
}

$.when(
    getLatestNews(), 
    getLatestReactions(), 
    prepareInterface()
).then(function() {
    console.log('fire after requests succeed');
    $('.finished').html('I am done!');
}).fail(function() {
    console.log('something went wrong!');
});

I made a small fiddle where you can check out the code.

我做了一个小小提琴,你可以看看代码。

http://jsfiddle.net/saifbechan/BKTwT/

You can check out a running copy there, I took the snippet from this tutorial, it's worth reading the whole tutorial, lot's of good information on asynchronous ajax.

你可以在那里查看正在运行的副本,我从本教程中获取了片段,值得阅读整个教程,很多关于异步ajax的好信息。

http://msdn.microsoft.com/en-us/scriptjunkie/gg723713

#1


2  

you're doing it wrong anyways :)

你反正做错了:)

here is one of the things that can happen: imagine the scripts are cached, then they might be loaded in no time. so, straight after the first call $.getScript( 'js/script1.js' ); the script will be available and $.ajaxStop (might!!!) get called, in the worst case that would happen three times.

这是可能发生的事情之一:想象一下脚本是否被缓存,然后它们可能会立即被加载。所以,直接在第一次调用$ .getScript('js / script1.js')之后;脚本将可用并且$ .ajaxStop(可能!!!)被调用,在最坏的情况下会发生三次。

to answer your question indirectly i would propose a different solution which avoids this race condition alltogether. you can try it here: http://jsfiddle.net/etGPc/8/

为了间接回答你的问题,我会提出一个完全避免这种竞争条件的不同解决方案。你可以在这里试试:http://jsfiddle.net/etGPc/8/

var urls, log, loaded;

// urls to load
urls = [
    'https://raw.github.com/h5bp/html5-boilerplate/master/js/script.js',
    'https://raw.github.com/h5bp/html5-boilerplate/master/js/plugins.js',
    'https://raw.github.com/h5bp/html5-boilerplate/master/js/libs/modernizr-2.0.6.min.js'
];

// urls loaded 
loaded = []; 


log = $( '#log' ); 

$.map( urls, function( url ){
    $.getScript( url, function(){
        // append to loaded urls
        loaded.push( url ); 
        log.append( "loaded " + url + "<br>" ); 

        // all loaded now? 
        if( loaded.length == urls.length ){
            log.append( "<b>all done!</b>" ); 
        }
    } ); 
} ); 

if you haven't seen jQuery.map before: it's not really different from a for-loop :)

如果您之前没有见过jQuery.map:它与for循环没有什么不同:)

another advantage here is that this method doesn't get confused if you have other ajax requests going on at the same time.

这里的另一个优点是,如果您同时进行其他ajax请求,则此方法不会混淆。

p.s. to avoid naming-*es you can wrap the entire thing in a self-executing function, i.e.

附:为了避免命名冲突,你可以将整个事物包装在一个自动执行的函数中,即

function(){
    var urls, log, loaded; 

    ... all code here ... 
} (); 

Update: Refactored the code a bit...

更新:重新编写代码...

var urls, loadedUrls, log;

urls = [
    'https://raw.github.com/h5bp/html5-boilerplate/master/js/script.js',
    'https://raw.github.com/h5bp/html5-boilerplate/master/js/plugins.js',
    'https://raw.github.com/h5bp/html5-boilerplate/master/js/libs/modernizr-2.0.6.min.js'
];    

loadedUrls = [];

log = $( '#log' )[0];

urls.forEach(function ( url ) {
    $.getScript( url, function () {
        loadedUrls.push( url );

        $( log ).append( 'loaded ' + url + '<br>' );

        if( loadedUrls.length === urls.length ){
            $( log ).append( '<b>all done!</b>' );
        }
    });
});

Live demo: http://jsfiddle.net/etGPc/10/

现场演示:http://jsfiddle.net/etGPc/10/

#2


3  

I digged a little more into this issue and it's indeed the cross-domain script request that's the caveat. As I posted in the comments, that scenario has been implemented such that it sets the global option to false. This makes jQuery not to fire global ajax events. (No idea why that has been implemented though.)

我在这个问题上多挖了一点,这确实是跨域脚本请求,这是一个警告。正如我在评论中发布的那样,该场景已经实现,因此它将全局选项设置为false。这使得jQuery不会触发全局ajax事件。 (不知道为什么会这样实现。)

This can be confirmed with this fiddle (pass means ajaxStop is fired):

这可以通过这个小提琴来确认(传递意味着ajaxStop被触发):

  • cross-domain, no script: pass
  • 跨域,没有脚本:通过

  • cross domain, script: fail
  • 跨域,脚本:失败

  • no cross-domain, no script: pass
  • 没有跨域,没有脚本:通过

  • no cross-domain, script: pass
  • 没有跨域,脚本:通过

The most straight-forward thing to do is simply adding another prefilter which forces the global option to true:

最直接的做法是简单地添加另一个prefilter,强制全局选项为true:

jQuery.ajaxPrefilter( "script", function() {
    s.global = true;
});

This also makes this failing scenario pass in the fiddle.

这也使得这种失败的场景在小提琴中传递。

#3


2  

While dealing with my own problem I found a much better and elegant solution to this problem, and it is using the jQuery deffered object. I think you already know about the function, maybe for another usecase, but it works great for loading files, and fire functions when eveything is done. Code is as follows:

在处理我自己的问题时,我发现了一个更好,更优雅的解决方案,它正在使用jQuery deffered对象。我想你已经知道了这个函数,可能是另一个用例,但它非常适合加载文件,并在完成eveything时触发函数。代码如下:

function getLatestNews() {
    return $.get('/echo/js/?delay=2&js=', function(data) {
        console.log('news data received');
        $('.news').css({'color':'blue'});
    });
}

function getLatestReactions() {
    return $.get('/echo/js/?delay=5&js=', function(data) {
        console.log('reactions data received');
        $('.reactions').css({'color':'green'});
    });
}

function prepareInterface() {
    return $.Deferred(function(dfd) {
        var latest = $('.news, .reactions');
        latest.slideDown(500, dfd.resolve);
        latest.addClass('active');
    }).promise();
}

$.when(
    getLatestNews(), 
    getLatestReactions(), 
    prepareInterface()
).then(function() {
    console.log('fire after requests succeed');
    $('.finished').html('I am done!');
}).fail(function() {
    console.log('something went wrong!');
});

I made a small fiddle where you can check out the code.

我做了一个小小提琴,你可以看看代码。

http://jsfiddle.net/saifbechan/BKTwT/

You can check out a running copy there, I took the snippet from this tutorial, it's worth reading the whole tutorial, lot's of good information on asynchronous ajax.

你可以在那里查看正在运行的副本,我从本教程中获取了片段,值得阅读整个教程,很多关于异步ajax的好信息。

http://msdn.microsoft.com/en-us/scriptjunkie/gg723713