Come lavori con una serie di jQuery Deferreds?


132

Ho un'applicazione che richiede che i dati vengano caricati in un certo ordine: l'URL radice, quindi gli schemi, quindi inizializzano infine l'applicazione con gli schemi e gli URL per i vari oggetti dati. Mentre l'utente naviga nell'applicazione, gli oggetti dati vengono caricati, convalidati rispetto allo schema e visualizzati. Quando l'utente CRUD trasmette i dati, gli schemi forniscono la convalida di primo passaggio.

Sto riscontrando un problema con l'inizializzazione. Uso una chiamata Ajax per recuperare l'oggetto root, $ .when (), e quindi creare una matrice di promesse, una per ogni oggetto dello 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, la clausola when () finale 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 (), come quando () costruirà un oggetto interno Deferred () per gestire l'elenco se nessun oggetto è passato.

Questo ha funzionato usando Futures.js. Come dovrebbe essere gestita una matrice di jQuery Deferreds, 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);
        });
    });

Ho quasi un problema identico, tranne per il fatto che devo attivare un metodo "successo" per ogni query Ajax in fetch_one, prima che "DONE" venga stampato. Come faresti a fare questo? Ho provato ad usare .pipe dopo "fetch_one", ma non sembra funzionare.
CambridgeMike,

Risposte:


198

Stai cercando

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

Funzionerà anche (per un certo valore di lavoro, non risolverà ajax rotto):

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

Dovrai passare $invece nullche in modo che thisdentro si $.whenriferisca jQuery. Non dovrebbe importare alla fonte ma è meglio che passare null.

Deriso tutti i tuoi $ .ajax sostituendoli con $.whene il campione funziona

Quindi è un problema nella tua richiesta Ajax o l'array che stai passando a fetch_schemas.


Grazie. In che modo questa sintassi è diversa da done (). Fail ()?
Elf Sternberg,

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 stesso non ha un metodo promise (), quindi viene ignorato a favore di un oggetto differito generato internamente (jQuery 1.5, linea 943).
Elf Sternberg,

1
@ElfSternberg è davvero irrilevante, ma per leggibilità non ho bisogno di dare una seconda occhiata a $.when.apply($, .... Il nullmi fa andare "Aspetta, cosa?". È una questione di stile e pratica di programmazione. Ho dovuto leggere la fonte per confermare thische non sarebbe stato lanciato un riferimento null all'interno di jQuery.quando!
Raynos,

7
L'uso di null mi fa pensare "ok, questa è una specie di soluzione" (che è), mentre se $ fosse usato la mia attenzione sarebbe dirottata sul pensare a che cosa fosse $.
Danyal Aytekin,

53

La soluzione precedente (grazie!) Non risolve correttamente il problema di recuperare gli oggetti forniti al resolve()metodo differito perché jQuery chiama done()e fail()callback con parametri individuali, non un array. Ciò significa che dobbiamo usare lo argumentspseudo-array per ottenere tutti gli oggetti risolti / rifiutati restituiti dall'array di differiti, il che è brutto:

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

Dato che abbiamo passato una serie di rinvii, sarebbe bello recuperare una serie di risultati. Sarebbe anche bello recuperare un array reale anziché uno pseudo-array in modo da poter usare metodi come Array.sort().

Ecco una soluzione ispirata when.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 puoi semplicemente passare un array di rinvii / promesse e recuperare un array di oggetti risolti / rifiutati nel tuo callback, 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");
});

@crispyduck - sai se puoi essere sicuro al 100% che l'ordine degli elementi dell'array in "schemas" var in then () sarà sempre nello stesso ordine che ajax chiama in "promises" var nel quando ()?
netpoetica,

6
Questo dovrebbe essere integrato in jQuery, ma - il team di jQuery ha respinto la richiesta più volte. Nel frattempo, le persone continuano a porre la domanda qui e ad aprire biglietti simili contro jQuery e finiamo con un'implementazione userland ovunque e / o chiamate imbarazzanti per apply()... andare a capire.
mindplay.dk,

Grazie per questa soluzione! C'è un modo per ottenere gli articoli di successo anche se uno (o più) fallito?
doktoreas,

bene tutto ciò che hai fatto qui è la argumentsmanipolazione nascosta nel suo metodo. Ottimo per il riutilizzo, ma non affronta la "bruttezza" di dover affrontare arguments(si potrebbe facilmente avere solo:var schemas=Array.prototype.slice.call(arguments);)
cowbert

2
@crispyduck, non dovresti deferred.fail(...)leggere deferred.reject(...)?
Bob S,

19

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

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

Maggiori informazioni sull'operatore di diffusione ES6 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator trova qui


1
Sì. Anche se quelli di noi che usano Coffeescript o uno dei suoi discendenti / imitatori hanno avuto accesso a quell'operatore per un po 'di tempo.
Elf Sternberg,

0

si 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)
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.