jQuery: загружать скрипты по порядку - PullRequest
10 голосов
/ 15 марта 2012

Я пытаюсь загрузить некоторые скрипты динамически с помощью jQuery:

var scripts = ['script1.js','script2.js','script3.js'];

$.each(scripts , function(i, val) {
     $.getScript(val, function() {
     console.log('loaded '+ val);
});

Но иногда порядок загружаемых скриптов меняется.Как я могу загрузить каждый скрипт после того, как предыдущий был успешно загружен?

Ответы [ 6 ]

17 голосов
/ 15 марта 2012

Вы можете загрузить каждую после того, как предыдущая закончила загрузку, используя функцию обратного вызова $.getScript() в качестве рекурсивного вызова функции.

//setup array of scripts and an index to keep track of where we are in the process
var scripts = ['script1.js','script2.js','script3.js'],
    index   = 0;

//setup a function that loads a single script
function load_script() {

    //make sure the current index is still a part of the array
    if (index < scripts.length) {

        //get the script at the current index
        $.getScript(scripts[index], function () {

            //once the script is loaded, increase the index and attempt to load the next script
            console.log('Loaded: ' + scripts[index]);
            index++;
            load_script();
        });
    }
}

То, что происходит в вашем коде, заключается в том, что скрипты запрашиваются одновременно и, поскольку они загружаются асинхронно, они возвращаются и выполняются в произвольном порядке.

Обновление

Я не проверял это, но если сценарии размещаются локально, вы можете попытаться получить их в виде простого текста, а затем сохранить весь код в переменных до тех пор, пока они не будут загружены, и тогда вы сможете оценить сценарии по порядку:

var scripts   = ['script1.js','script2.js','script3.js'],

    //setup object to store results of AJAX requests
    responses = {};

//create function that evaluates each response in order
function eval_scripts() {
    for (var i = 0, len = scripts.length; i < len; i++) {
        eval(responses[scripts[i]]);
    }
}

$.each(scripts, function (index, value) {
    $.ajax({
        url      : scripts[index],

        //force the dataType to be `text` rather than `script`
        dataType : 'text',
        success  : function (textScript) {

            //add the response to the `responses` object
            responses[value] = textScript;

            //check if the `responses` object has the same length as the `scripts` array,
            //if so then evaluate the scripts
            if (responses.length === scripts.length) { eval_scripts(); }
        },
        error    : function (jqXHR, textStatus, errorThrown) { /*don't forget to handle errors*/ }
    });
});
10 голосов
/ 15 марта 2012

Вы используете тот факт, что jqXhr объект , возвращаемый $.getScript (и всеми другими методами Ajax), реализует интерфейс Promise и, следовательно, предоставляет .pipe(), который можно использовать для отложенной цепочки объекты:

var deferred = new $.Deferred(),
    pipe = deferred;

$.each(scripts , function(i, val) {
     pipe = pipe.pipe(function() {
         return  $.getScript(val, function() {
             console.log('loaded '+ val);
         });
     });
});

deferred.resolve();

Для получения дополнительной информации посмотрите отложенные объекты и deferred.pipe.

В целом, использование отложенных объектов дает вам большую гибкость и может упростить добавление обработчиков ошибок.

2 голосов
/ 05 ноября 2013

улучшенная версия решения @ Jasper:

  • с использованием $.when для синхронизации вызовов.
  • с использованием global eval.

Вот код:

/**
 * Load scripts in parallel keeping execution order.
 * @param {array} An array of script urls. They will parsed in the order of the array.
 * @returns {$.Deferred}
 */
function getScripts(scripts) {
    var xhrs = scripts.map(function(url) {
        return $.ajax({
            url: url,
            dataType: 'text',
            cache: true
        });
    });

    return $.when.apply($, xhrs).done(function() {
        Array.prototype.forEach.call(arguments, function(res) {
            eval.call(this, res[0]);
        });
    });
}

Суть: https://gist.github.com/ngryman/7309432.

1 голос
/ 15 марта 2012

Некоторые сценарии могут различаться по размеру, поэтому они непредсказуемы.Попробуйте что-то вроде этого.

var scripts = ['script1.js','script2.js','script3.js'], ScriptIndex = 0;
function LoadScript(index){
    if(index >= scripts.length)
        return;

    $.getScript(scripts[index], function(){
        console.log('Loaded script '+ scripts[index]);
        LoadScript(++ScriptIndex);
    }
}

Это должно загрузить каждый скрипт после того, как последний полностью загрузится.Убедитесь, что вы запустили его, вызвав функцию LoadScript(0);

0 голосов
/ 05 апреля 2018

Есть небольшая проблема с решением @ ngryman; если в массиве URL-адресов имеется только один URL-адрес для загрузки, аргументом аргумента функции .done () будет одномерный массив, а не 2-мерный. Вам нужно проверить аргументы перед циклом, таким образом:

/**
 * Load scripts in parallel keeping execution order.
 * @param {array} An array of script urls. They will parsed in the order of the array.
 * @returns {$.Deferred}
 */
function getScripts(scripts) {
    var xhrs = scripts.map(function(url) {
        return $.ajax({
            url: url,
            dataType: 'text',
            cache: true
        });
    });

    return $.when.apply($, xhrs).done(function() {
        if (arguments.length == 3 && typeof arguments[0] == "string") {
            arguments = [arguments];
        }
        Array.prototype.forEach.call(arguments, function(res) {
            eval.call(this, res[0]);
        });
    });
}

Извиняюсь за новый ответ, недостаточно реп, чтобы опубликовать комментарий к ответу @ ngryman, но подумал, что это может кому-то помочь!

0 голосов
/ 15 марта 2012

Вы можете отсортировать () массив -

var sorted = scripts.sort();

, а затем используйте сортировку в каждой функции.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...