2012-03-14 6 views
9

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

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

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

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

ответ

16

Вы можете загрузить каждый после того, как предыдущая завершила загрузку, используя функцию обратного вызова $.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(); 
     }); 
    } 
} 

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

Update

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

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*/ } 
    }); 
}); 
+0

Ahh, ninja'd. Хорошая работа. –

+0

это, кажется, работает !, спасибо и очень приятное объяснение! – greenbandit

0

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

var sorted = scripts.sort(); 

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

+0

Я считаю, что вопрос OPs относится к порядку ответов на запросы AJAX. – Jasper

1

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

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

В чем разница между '++ ScriptIndex' и' ScriptIndex ++' (если они есть)? – Jasper

+0

'++ ScriptIndex' устанавливает, AND возвращает значение с добавочным значением, тогда как' ScriptIndex ++ 'просто устанавливает добавочное значение. –

+1

gotcha, спасибо. – Jasper

10

Вы делаете использовать на самом деле jqXhr object возвращаемый $.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 objects и deferred.pipe.

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

+0

Поскольку я узнал о отсроченных, я их люблю. Но почему-то я пропустил функцию трубы. Отличный пост! – roberkules

+0

Это бесполезно - вы в основном делаете '$ .when ($ .getScript (a), $ .getScript (b))' - 'scripts' по-прежнему сохраняются сразу после их загрузки с сервера, независимо от того, был ли предыдущий уже загружен или нет. – Dwelle

+0

@Dwellee: Нет, это не тот случай. '$ .getScript (b)' вызывается внутри обратного вызова '.pipe', который вызывается только после загрузки' $ .getScript (a) '(был ли он оценен, я не уверен). –

2

усиливается версия @ решения Джаспера:

  • с помощью $.when для синхронизации вызовов.
  • с использованием глобальных 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.

Смежные вопросы