8

Depuis que j'ai utilisé $.Deferred, j'ai rencontré ce scénario plusieurs fois: j'ai une liste de valeurs qui chacune donne un objet différé d'une certaine manière et je veux exécuter un rappel une fois que tous les Les objets différés sont résolus.Traitement des tableaux d'objets différés

serait quelque chose comme un exemple plus concret ceci:

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 
}); 

Y at-il une solution plus élégante que le code dans mon exemple?

+2

Pourquoi pensez-vous que ce n'est pas élégant? – topek

+0

Après avoir écrit un code comme celui-ci pour la 3ème fois, j'ai commencé à penser que c'était un scénario assez commun et que je pourrais être traité par le framework Deferred Objects d'une meilleure façon que je ne faisais que négliger. –

Répondre

3

Oui, vous ne devriez jamais faire référence à une valeur de référence dans une boucle. Toujours faire une copie.

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 
}); 

Mais sérieusement, je suis juste joshin 'vous. Ce code bascule. Restez avec.

+1

Vous avez raison à propos de la valeur de recherche, mais l'avoir dans la déclaration (c'est-à-dire 'var i = 0, j = urls.length') la met en cache. Ce que vous voulez éviter, c'est de l'avoir dans la comparaison (c'est-à-dire «i

0

est ici une fonction d'aide que j'ai écrit appelé LoadInitialData, on peut l'appeler comme ça 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); 
3

Une façon plus élégante d'écrire cet exemple est la fonction de carte de tableau (ou jQuery de .map $):

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 
}); 

Vous pouvez même rouler votre propre "whenDone" et les fonctions "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 
}); 
+0

J'aime l'utilisation de 'Array.prototype.map'. C'est plus propre. –

Questions connexes