Passare un array di Deferreds di $.quando()

Ecco un esempio volutamente semplice di quello che sta succedendo: http://jsfiddle.net/adamjford/YNGcm/20/

HTML:

<a href="#">Click me!</a>
<div></div>

JavaScript:

function getSomeDeferredStuff() {
    var deferreds = [];

    var i = 1;
    for (i = 1; i <= 10; i++) {
        var count = i;

        deferreds.push(
        $.post('/echo/html/', {
            html: "<p>Task #" + count + " complete.",
            delay: count
        }).success(function(data) {
            $("div").append(data);
        }));
    }

    return deferreds;
}

$(function() {
    $("a").click(function() {
        var deferreds = getSomeDeferredStuff();

        $.when(deferreds).done(function() {
            $("div").append("<p>All done!</p>");
        });
    });
});

Voglio “Tutto fatto!” dopo tutte le differite compiti sono stati completati, ma $.when() non sembra sapere come gestire un array di tali oggetti. “Tutto fatto!” sta accadendo primo perché l’array non è un oggetto Rinviato, quindi, jQuery va avanti e non si assume è appena fatto.

So che si potrebbe passare gli oggetti in funzione di come $.when(deferred1, deferred2, ..., deferredX) ma non si sa quante Differite oggetti ci sarà in esecuzione il vero problema sto cercando di risolvere.

correlate: in Attesa di più differita oggetti per completare
Aggiunto un nuovo e più semplice, la risposta per questa domanda qui sotto. Ti do non bisogno di utilizzare un array o $.when.apply a tutti per ottenere lo stesso risultato.
il rollback questione, come è stato troppo specifico (questo non è solo un AJAX problema)

OriginaleL’autore adamjford | 2011-04-11

8 Replies
  1. 677

    Per passare un array di valori qualsiasi funzione che normalmente si aspetta loro di essere i singoli parametri utilizzare Function.prototype.apply, quindi in questo caso è necessario:

    $.when.apply($, my_array).then( ___ );

    Vedere http://jsfiddle.net/YNGcm/21/

    In ES6, è possibile utilizzare il ... diffusione operatore invece:

    $.when(...my_array).then( ___ );

    In entrambi i casi, dal momento che è improbabile che abbiate mai conosciuto in anticipo quanti parametri formali la .then gestore richiederà, che il gestore avrebbe bisogno di elaborare il arguments matrice al fine di recuperare il risultato di ogni promessa.

    Questo funziona, è impressionante. 🙂 Mi meraviglio che non ero in grado di dredge up ad un semplice cambiamento tramite Google!
    questo perché è un metodo generico, non specifico per $.whenf.apply(ctx, my_array) chiamerà f con this == ctx e gli argomenti impostare il contenuto di my_array.
    Io sono un po ‘ imbarazzato, che io non conoscevo questo metodo, considerando quanto tempo ho scritto in JavaScript ora!
    FWIW, il collegamento in Eli risposta a un earler domanda con la discussione di passaggio $ vs null come primo parametro è la pena di leggere. In questo particolare caso non importa, però.
    Sì, ma $ è meno tipizzazione di null e sei al sicuro quando $.when attuazione delle modifiche (che non è probabile in questo caso, ma perché non tenere this invariato per impostazione predefinita).

    OriginaleL’autore Alnitak

  2. 99

    Soluzioni sopra (grazie!) non affrontare adeguatamente il problema di ottenere indietro gli oggetti fornito per la differita del resolve() metodo jQuery chiama il done() e fail() di callback con i singoli parametri, non un array. Questo significa che noi dobbiamo utilizzare il arguments pseudo-array per ottenere tutti risolti/respinto oggetti restituiti dalla matrice di deferreds, che è brutto:

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

    Dal momento che abbiamo passato in una matrice di deferreds, sarebbe bello ottenere un array di risultati. Sarebbe anche bello tornare un effettivo array invece di una pseudo-array in modo che possiamo utilizzare metodi come Array.sort().

    Qui è una soluzione ispirata when.js‘s when.all() metodo che risolve questi problemi:

    //Put somewhere in your scripting environment
    if (typeof jQuery.when.all === 'undefined') {
        jQuery.when.all = function (deferreds) {
            return $.Deferred(function (def) {
                $.when.apply(jQuery, deferreds).then(
                    function () {
                        def.resolveWith(this, [Array.prototype.slice.call(arguments)]);
                    },
                    function () {
                        def.rejectWith(this, [Array.prototype.slice.call(arguments)]);
                    });
            });
        }
    }

    Ora si può semplicemente passare un array di deferreds/promesse per ottenere un array di risolti/oggetti rifiutati nella richiamata, come di seguito:

    $.when.all(deferreds).then(function(objects) {
        console.log("Resolved objects:", objects);
    });
    Si potrebbe desiderare di utilizzare resolveWith e rejectWith solo così si ottiene la stessa originale deferreds come ‘questo’ differita.resolveWith(questo, [Array.prototipo.fetta.chiamata(argomenti)]) ecc
    C’è solo un piccolo problema con il tuo codice, quando c’è un solo elemento dell’array i risultati di matrice restituisce solo il risultato, invece di un array con un solo elemento (che rompono il codice che si aspetta un array). Per risolvere il problema, utilizzare questa funzione var toArray = function (args) { return deferreds.length > 1 ? $.makeArray(args) : [args]; } invece di Array.prototype.slice.call.
    Hm, questo non sembra prendere qualsiasi 404.
    Trovato il motivo .esito negativo dovrebbe essere .rifiuta invece – in modo che possa prendere 404.
    Esattamente quello che mi serviva, grazie!

    OriginaleL’autore crispyduck

  3. 38

    È possibile applicare il when metodo a matrice:

    var arr = [ /* Deferred objects */ ];
    
    $.when.apply($, arr);

    Come si fa a lavorare con una serie di jQuery Deferreds?

    Wow, io sono lento oggi…
    In realtà ho visto che la domanda, ma credo che tutti i dettagli aggiuntivi in questione che ha causato la risposta al mio problema (che è giusto) per volare a destra sopra la mia testa.
    se ti fa sentire meglio, ho trovato la tua domanda più facile consumare (e prima sul mio particolare di ricerca di Google per questo problema).
    Felice di sentire che lei ha aiutato!
    Questa è una grande risposta, ma non è chiaro a me come è il caso per esempio della domanda originale. Dopo aver consultato il collegato domanda, è diventato chiaro che la linea “$.quando(deferreds).fatto(function() {” dovrebbe essere modificato in “$.quando.applicare la($,deferreds).fatto(function() {“. Giusto?

    OriginaleL’autore Eli

  4. 5

    Come alternativa più semplice, che non richiede $.when.apply o un array, è possibile utilizzare il seguente schema per generare una sola promessa per di più parallelo promesse:

    promise = $.when(promise, anotherPromise);

    ad esempio

    function GetSomeDeferredStuff() {
        //Start with an empty resolved promise (or undefined does the same!)
        var promise;
        var i = 1;
        for (i = 1; i <= 5; i++) {
            var count = i;
    
            promise = $.when(promise,
            $.ajax({
                type: "POST",
                url: '/echo/html/',
                data: {
                    html: "<p>Task #" + count + " complete.",
                    delay: count / 2
                },
                success: function (data) {
                    $("div").append(data);
                }
            }));
        }
        return promise;
    }
    
    $(function () {
        $("a").click(function () {
            var promise = GetSomeDeferredStuff();
            promise.then(function () {
                $("div").append("<p>All done!</p>");
            });
        });
    });

    Note:

    • Ho capito questo fuori dopo aver visto qualcuno catena promesse in sequenza, utilizzando promise = promise.then(newpromise)
    • Il rovescio della medaglia è che crea ulteriore promessa oggetti dietro le quinte e gli eventuali parametri passati alla fine non sono molto utili (come sono nidificati all’interno di altri oggetti). Per ciò che si desidera, anche se è breve e semplice.
    • Il vantaggio è che non richiede alcun matrice o array di gestione.
    Mi corregga se sbaglio, ma il tuo approccio è efficace nidificazione $.quando( $.quando( $.quando(…) ) ) e così si finisce in modo ricorsivo nidificato 10 livelli di profondità se c’è di 10 iterazioni. Questo non mi sembra molto in parallelo, come si deve aspettare per ogni livello di ritorno di un minore nidificato promessa prima di poter tornare la sua stessa promessa, credo che l’approccio di matrice accettate risposta è molto più “pulito”, come si usa il flessibile dei parametri di comportamento integrato nel $.quando() metodo.
    questo è destinato a fornire una alternativa più semplice per la codifica, non le prestazioni (che è trascurabile con più Async coding), come è stato fatto con il concatenamento then() chiama in un modo simile. Il comportamento con $.when è quello di agire come si è parallelo (non concatenate). Si prega di provare prima di buttare via un utile alternativa come funziona 🙂
    Cavalli per i corsi. Si sono certamente il diritto di opinione, ma voi avete, ovviamente, non ha usato questo di te. La mia opinione è basata sull’uso pratico di questa tecnica. opere e ha usi, quindi perché buttare fuori uno strumento basato su esagerazioni come “un sacco di avvertimenti” (uno) e “non risolve nulla” (non è vero – elimina la matrice di trasformazione e semplifica in cascata, in parallelo promesse in cui i valori di ritorno non sono necessità, che come dovresti sapere, sono raramente utilizzati nella elaborazione parallela dei casi). Downvotes sono supposti per essere “questa non è una risposta utile” 🙂
    Ciao @GoneCoding. Posso chiedere a che non si aggiunge il voto commento per le vostre risposte? Che è adatto per i commenti, ma per il resto è rumore che distrae dall’altrimenti un buon contenuto. Grazie.
    Io non postare più, ma io sono infastidito l’ignoranza visualizzato per nulla originale. Mantenere tutte le nuove idee, per me al giorno d’oggi 🙂

    OriginaleL’autore Gone Coding

  5. 3

    Voglio proporre uno con utilizzo di $.ogni:

    1. Possiamo dichiarare ajax funzione come:

      function ajaxFn(someData) {
          this.someData = someData;
          var that = this;
          return function () {
              var promise = $.Deferred();
              $.ajax({
                  method: "POST",
                  url: "url",
                  data: that.someData,
                  success: function(data) {
                      promise.resolve(data);
                  },
                  error: function(data) {
                      promise.reject(data);
                  }
              })
              return promise;
          }
      }
    2. Parte di codice in cui abbiamo la creazione di array di funzioni con l’ajax per inviare:

      var arrayOfFn = [];
      for (var i = 0; i < someDataArray.length; i++) {
          var ajaxFnForArray = new ajaxFn(someDataArray[i]);
          arrayOfFn.push(ajaxFnForArray);
      }
    3. E le funzioni di chiamata con l’invio di ajax:

      $.when(
          $.each(arrayOfFn, function(index, value) {
              value.call()
          })
      ).then(function() {
              alert("Cheer!");
          }
      )

    OriginaleL’autore Volodymyr Yasinskyi

  6. 1

    Se sei transpiling e avere accesso a ES6, è possibile utilizzare diffusione sintassi quali, in particolare, si applica in ogni caso iterable elemento di un oggetto come un discreto argomento, solo il modo in cui $.when() esigenze.

    $.when(...deferreds).done(() => {
        //do stuff
    });

    MDN Link di Diffusione di Sintassi

    OriginaleL’autore relic

  7. 0

    Se si utilizza angularJS o qualche variante di Q promessa biblioteca, poi si dispone di un .all() metodo che risolve questo problema esatto.

    var savePromises = [];
    angular.forEach(models, function(model){
      savePromises.push(
        model.saveToServer()
      )
    });
    
    $q.all(savePromises).then(
      function success(results){...},
      function failed(results){...}
    );

    vedere le API:

    https://github.com/kriskowal/q/wiki/API-Reference#promiseall

    https://docs.angularjs.org/api/ng/service/$q

    Questo è del tutto irrilevante.
    In che modo? Tutti i javascript promessa librerie di condividere un simile API, e non c’è niente di sbagliato con che mostra le diverse implementazioni. Sono arrivato a questa pagina cercando una risposta angolare, e ho il sospetto che molti altri utenti potranno accedere a questa pagina e non necessariamente essere in jquery solo ambiente.
    Cioè, perché jQuery promesse di non condividere questa API, questo è completamente inadeguato come una risposta su Stack Overflow – ci sono risposte simili per Angolare e si può chiedere. (Per non parlare, si dovrebbe .map qui, ma vabbè).

    OriginaleL’autore mastaBlasta

  8. 0

    Ho avuto un caso molto simile in cui mi distacco in ogni ciclo e quindi impostando il markup html in alcuni campi da numeri ricevuti dall’ajax. Ho poi bisogno di fare una somma di (aggiornata) i valori di questi campi e posto in un campo totale.

    Così il problema era che stavo cercando di fare una somma di tutti i numeri, ma i dati non erano arrivati ancora tornato dalla async chiamate ajax. Necessario per completare questa funzionalità in un paio di funzioni per essere in grado di riutilizzare il codice. La mia funzione esterna attende i dati prima ho poi andare e fare alcune cose con il completamente aggiornato DOM.

        //1st
        function Outer() {
            var deferreds = GetAllData();
    
            $.when.apply($, deferreds).done(function () {
                //now you can do whatever you want with the updated page
            });
        }
    
        //2nd
        function GetAllData() {
            var deferreds = [];
            $('.calculatedField').each(function (data) {
                deferreds.push(GetIndividualData($(this)));
            });
            return deferreds;
        }
    
        //3rd
        function GetIndividualData(item) {
            var def = new $.Deferred();
            $.post('@Url.Action("GetData")', function (data) {
                item.html(data.valueFromAjax);
                def.resolve(data);
            });
            return def;
        }

    OriginaleL’autore Cameron Forward

Lascia un commento