#jquery #jquery-deferred #deferred-execution #deferred-loading
#jquery #jquery-отложенный #отложенное выполнение #отложенная загрузка
Вопрос:
С момента использования $.Deferred
я пару раз сталкивался с этим сценарием: у меня есть список значений, каждое из которых каким-то образом дает отложенный объект, и я хочу выполнить обратный вызов, как только все отложенные объекты будут разрешены.
Более конкретным примером может быть что-то вроде этого:
var urls = [ 'foo.com', 'bar.com', 'baz.com', 'qux.com' ],
defers = [], defer;
for( var i = 0, j = urls.length; i < j; i ){
defer = $.ajax({
url: 'http://' urls[ i ]
});
defers.push(defer);
}
$.when.apply(window, defers).done(function(){
// Do Something
});
Есть ли более элегантное решение, чем код в моем примере?
Комментарии:
1. Почему вы думаете, что это не элегантно?
2. После написания подобного кода в 3-й раз я начал думать, что это довольно распространенный сценарий, и он может быть обработан платформой отложенных объектов лучшим способом, который я просто упускал из виду.
Ответ №1:
Да, вы никогда не должны ссылаться на значение поиска в цикле. Всегда делайте копию.
var urls = [ 'foo.com', 'bar.com', 'baz.com', 'qux.com' ],
defers = [], defer;
var urlsLength = urls.length;
for( var i = 0, j = urlsLength; i < j; i ){
defer = $.ajax({
url: 'http://' urls[ i ]
});
defers.push(defer);
}
$.when.apply(window, defers).done(function(){
// Do Something
});
Но если серьезно, я просто издеваюсь над тобой. Этот код потрясает. Придерживайтесь этого.
Комментарии:
1. Вы правы в отношении значения поиска, но наличие его в объявлении (т.Е.
var i = 0, j = urls.length
) кэширует его. Чего вы хотите избежать, так это наличия его в сравнении (т.е.i < urls.length
). 🙂
Ответ №2:
Более элегантный способ написания этого примера — использовать функцию отображения массива (или $.map от jQuery):
var urls = [ 'foo.com', 'bar.com', 'baz.com', 'qux.com' ];
var defers = urls.map( function( url) {
return $.ajax({
url: 'http://' url
});
});
$.when.apply(window, defers).done(function(){
// Do Something
});
Вы даже можете использовать свои собственные функции «whenDone» и «fetchURL»:
Array.prototype.whenDone = function(callback){
return $.when.apply(window, this).done(callback);
}
function fetchURL(url){
return $.ajax({
url: 'http://' url
});
}
var urls = [ 'foo.com', 'bar.com', 'baz.com', 'qux.com' ];
urls.map( fetchUrl ).whenDone(function(){
// Do Something
});
Ответ №3:
Вот вспомогательная функция, которую я написал, называется LoadInitialData, ее можно вызвать следующим образом LoadInitialData(urlArray, dataReturnedArray, callback)
///
/// 1. The magical function LoadInitialData
///
///
/// <summary>
/// This functions allows you to fire off a bunch of ajax GET requests and run a callback function when
/// all the requests come back that contains an array of all your ajax success data
/// </summary>
/// <params>
/// urlArray - an array of urls to be looped and ajaxed
/// dataReturnedArray - this array will contain all data returned from your ajax calls. Its stuctured like this
/// [{url: "http//site.com/1", "data": "your data"}, {url: "http//site.com/2", "data": "your data"}]
/// dataReturnedArray[0] is data from call 1, dataReturnedArray[1] is data from call 2 etc. It might be a
/// good idea to pass in a global array so you can use this data throughout your application.
/// callback - a function that runs after all ajax calles are done, dataReturnedArray is available in the callback
/// </parms>
///
function LoadInitialData(urlArray, dataReturnedArray, callback){
// set up a deffered promise to fire when all our async calls come back
var urls = urlArray, defers = [], defer;
var urlsLength = urls.length;
for( var i = 0, j = urlsLength; i < j; i ){
var u = urls[ i ];
defer = $.ajax({
type : "GET",
dataType : "jsonp",
url: u,
success: function(data){
dataReturnedArray.push({
url: u,
data: data
});
}
});
defers.push(defer);
}
$.when.apply(window, defers).then(function(){
// Do Something now that we have all the data
console.log("done fetching all data");
callback(dataReturnedArray);
});
}
///
/// 2. Your config…. urlArray, dataReturnedArray, callback
///
var app = app || {};
app.data = []; // will hold the fetched data
var urlArr = ["http://site.com/2", "http://site.com/2"]; // the urls to get data from
// function to call once all the data is loaded
callback = function(data){
// data cleansing
var tblData = [];
$.each(data, function(key, value){
$.each(value.data, function(key, value){
tblData.push(value);
});
});
$("#loader").hide();
};
///
/// 3. Kick it all off!
///
// show a loader here
$("#loader").show();
// fire off the code to fetch the initial data
LoadInitialData(urlArr, app.data, callback);