2011-02-02 9 views
128

Possiedo un'applicazione che richiede il caricamento dei dati in un determinato ordine: l'URL di root, quindi gli schemi, quindi infine l'inizializzazione dell'applicazione con gli schemi e gli URL per i vari oggetti dati. Mentre l'utente naviga l'applicazione, gli oggetti dati vengono caricati, convalidati rispetto allo schema e visualizzati. Come CRUD utente i dati, gli schemi forniscono la convalida di primo passaggio.Come si lavora con una serie di jQuery differite?

Ho un problema con l'inizializzazione. Io uso una chiamata Ajax per recuperare l'oggetto radice, $ .when(), e quindi creare una serie di promesse, una per ogni oggetto schema. Che funzioni. Vedo il recupero nella console.

Vedo quindi il recupero per tutti gli schemi, quindi ogni chiamata $ .ajax() funziona. fetchschemas() in effetti restituisce una serie di promesse.

Tuttavia, quella finale quando la clausola() non viene mai attivata e la parola "DONE" non viene mai visualizzata sulla console. Il codice sorgente di jquery-1.5 sembra implicare che "null" sia accettabile come oggetto da passare a $ .when.apply(), poiché when() costruirà un oggetto interno Deferred() per gestire l'elenco se nessun oggetto è passato.

Questo ha funzionato con Futures.js. Come dovrebbe essere gestito un array di jQuery Deferred, se non in questo modo?

var fetch_schemas, fetch_root; 

    fetch_schemas = function(schema_urls) { 
     var fetch_one = function(url) { 
      return $.ajax({ 
       url: url, 
       data: {}, 
       contentType: "application/json; charset=utf-8", 
       dataType: "json" 
      }); 
     }; 

     return $.map(schema_urls, fetch_one); 
    }; 

    fetch_root = function() { 
     return $.ajax({ 
      url: BASE_URL, 
      data: {}, 
      contentType: "application/json; charset=utf-8", 
      dataType: "json" 
     }); 
    }; 

    $.when(fetch_root()).then(function(data) { 
     var promises = fetch_schemas(data.schema_urls); 
     $.when.apply(null, promises).then(function(schemas) { 
      console.log("DONE", this, schemas); 
     }); 
    }); 
+0

Ho quasi un problema identico, tranne che ho bisogno di sparare un metodo di "successo" per ogni query ajax in fetch_one, prima che venga stampato "DONE". Come faresti a fare questo? Ho provato a usare .pipe dopo "fetch_one", ma non sembrava funzionare. – CambridgeMike

risposta

187

Siete alla ricerca di

$.when.apply($, promises).then(function(schemas) { 
    console.log("DONE", this, schemas); 
}, function(e) { 
    console.log("My ajax failed"); 
}); 

Questo funziona anche (per alcuni valore del lavoro, non risolverà ajax rotto):

$.when.apply($, promises).done(function() { ... }).fail(function() { ... });` 

vorrete per passare $ anziché null in modo che this all'interno di $.when si riferisca a jQuery. Non dovrebbe importare alla fonte ma è meglio quindi passare null.

preso in giro tutti i vostri $ .ajax sostituendoli con $.when e il campione works

Quindi è sia un problema nella vostra richiesta Ajax o la matrice tuo passaggio a fetch_schemas.

+0

Grazie. In che modo questa sintassi è diversa da done(). Fail()? –

+2

@elf Sternberg, '.then (a, b) === .done (a) .fail (b)' è una scorciatoia pigra. Puoi chiamare '.done (a) .fail (b)' se vuoi – Raynos

+1

Oh, e l'uso di $ .when.apply ($, ...) e $ .when.apply (null, ...) sembra essere irrilevante. jQuery non ha un metodo promise(), quindi viene ignorato a favore di un oggetto Deferred generato internamente (jQuery 1.5, riga 943). –

50

La soluzione precedente (grazie!) Non risolve adeguatamente il problema di tornare oggetti forniti al resolve() metodo differito perché jQuery chiama i done() e fail() callback con i singoli parametri, non un array. Ciò significa che dobbiamo usare il arguments pseudo-array per ottenere tutti gli oggetti risolti/respinti restituiti dalla schiera di deferreds, che è brutto:

$.when.apply($, promises).then(function() { 
    var schemas=arguments; // The array of resolved objects as a pseudo-array 
    ... 
}; 

Essendo passato in una serie di deferreds, sarebbe bello per ottenere una serie di risultati. Sarebbe anche bello recuperare una matrice reale invece di uno pseudo-array, così possiamo usare metodi come Array.sort().

Ecco una soluzione ispirata a quando.js s' when.all() metodo che affronta questi problemi:

// Put somewhere in your scripting environment 
if (jQuery.when.all===undefined) { 
    jQuery.when.all = function(deferreds) { 
     var deferred = new jQuery.Deferred(); 
     $.when.apply(jQuery, deferreds).then(
      function() { 
       deferred.resolve(Array.prototype.slice.call(arguments)); 
      }, 
      function() { 
       deferred.fail(Array.prototype.slice.call(arguments)); 
      }); 

     return deferred; 
    } 
} 

Ora si può semplicemente passare in una serie di deferreds/promesse e tornare una serie di risolto/respinto oggetti nel richiamata, in questo modo:

$.when.all(promises).then(function(schemas) { 
    console.log("DONE", this, schemas); // 'schemas' is now an array 
}, function(e) { 
    console.log("My ajax failed"); 
}); 
+0

Wow, funziona perfettamente; grande idea. Grazie! –

+0

@crispyduck: sai se puoi essere sicuro al 100% che l'ordine degli elementi dell'array negli "schemi" var in then() sarà sempre nello stesso ordine delle chiamate ajax nella variabile "promises" nel quando()? – netpoetica

+1

questo mi ha salvato il culo, grazie! –

17

Se si utilizza la versione ES6 di javascript Esiste un operatore di spread (...) che converte la matrice di oggetti in argomenti separati da virgole.

$.when(...promises).then(function() { 
var schemas=arguments; 
}; 

più su ES6 operatore diffusione https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator trovare

+0

Sì. Sebbene quelli di noi che usano Coffeescript o uno dei suoi discendenti/imitatori abbiano avuto accesso a quell'operatore per un po 'di tempo. –

+0

non riesci a diffondere anche i tuoi schemi? – matthewdaniel

0

estende quando con questo codice:

var rawWhen = $.when 
$.when = function(promise) { 
    if ($.isArray(promise)) { 
     var dfd = new jQuery.Deferred() 
     rawWhen.apply($, promise).done(function() { 
      dfd.resolve(Array.prototype.slice.call(arguments)) 
     }).fail(function() { 
      dfd.reject(Array.prototype.slice.call(arguments)) 
     }) 
     return dfd.promise() 
    } else { 
     return rawWhen.apply($, arguments) 
    } 
} 
Problemi correlati