Jak pracujesz z tablicą jQuery Deferreds?

132

Mam aplikację, która wymaga załadowania danych w określonej kolejności: główny adres URL, następnie schematy, a na końcu zainicjuj aplikację ze schematami i adresami URL dla różnych obiektów danych. Gdy użytkownik nawiguje po aplikacji, obiekty danych są ładowane, sprawdzane względem schematu i wyświetlane. Gdy użytkownik CRUD dane, schematy zapewniają walidację pierwszego przejścia.

Mam problem z inicjalizacją. Używam wywołania Ajax, aby pobrać obiekt główny, $ .when (), a następnie tworzę tablicę obietnic, po jednej dla każdego obiektu schematu. To działa. Widzę pobieranie w konsoli.

Następnie widzę pobieranie dla wszystkich schematów, więc każde wywołanie $ .ajax () działa. fetchschemas () rzeczywiście zwraca tablicę obietnic.

Jednak ta ostatnia klauzula when () nigdy nie jest uruchamiana, a słowo „DONE” nigdy nie pojawia się na konsoli. Kod źródłowy jquery-1.5 wydaje się sugerować, że "null" jest akceptowalne jako obiekt do przekazania do $ .when.apply (), ponieważ when () zbuduje wewnętrzny obiekt Deferred () do zarządzania listą, jeśli żaden obiekt nie jest przeszedł.

To działało przy użyciu Futures.js. Jak należy zarządzać tablicą jQuery Deferreds, jeśli nie w ten sposób?

    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);
        });
    });
Elf Sternberg
źródło
Mam prawie identyczny problem, z wyjątkiem tego, że muszę uruchomić metodę „sukces” dla każdego zapytania Ajax w fetch_one, zanim zostanie wydrukowane „DONE”. Jak byś się tym zajął? Próbowałem użyć .pipe po „fetch_one”, ale to nie działa.
CambridgeMike

Odpowiedzi:

198

Szukasz

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

To również zadziała (dla pewnej wartości pracy nie naprawi zepsutego ajax):

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

Będziesz chciał przejść $zamiast tego null, aby odnosiło się do thiswnętrza . To nie powinno mieć znaczenia dla źródła, ale lepiej niż przejście .$.whenjQuerynull

Wyśmiewano wszystkie twoje $ .ajax, zastępując je $.wheni próbka działa

Więc jest to albo problem w twoim żądaniu Ajax, albo w tablicy, którą przekazujesz do fetch_schemas.

Raynos
źródło
Dziękuję Ci. Czym ta składnia różni się od done (). Fail ()?
Elf Sternberg
2
@elf Sternberg, .then(a,b) === .done(a).fail(b)to leniwy skrót. Możesz zadzwonić, .done(a).fail(b)jeśli chcesz
Raynos
1
Aha, i użycie znaków $ .when.apply ($, ...) i $ .when.apply (null, ...) wydaje się nie mieć znaczenia. Sama jQuery nie ma metody promise (), więc jest ignorowana na rzecz wewnętrznie wygenerowanego obiektu Deferred (jQuery 1.5, wiersz 943).
Elf Sternberg
1
@ElfSternberg to rzeczywiście nieistotne, ale ze względu na czytelność nie muszę na nie rzucać drugiego spojrzenia $.when.apply($, .... To nullsprawia, że ​​mówię „czekaj, co?”. To kwestia stylu i praktyki kodowania. Musiałem przeczytać źródło, aby potwierdzić, thisże nie wrzucę zerowej referencji do jQuery.when!
Raynos
7
Użycie null sprawia, że ​​myślę „ok, to jest rodzaj obejścia” (a tak jest), podczas gdy gdyby użyto $, moja uwaga byłaby skierowana na myślenie o wtf, dla którego $ był przeznaczony.
Danyal Aytekin
53

Powyższe obejście (dzięki!) Nie rozwiązuje poprawnie problemu odzyskiwania obiektów dostarczonych do metody deferred, resolve()ponieważ jQuery wywołuje wywołania zwrotne done()i fail()z indywidualnymi parametrami, a nie tablicą. Oznacza to, że musimy użyć argumentspseudo-tablicy, aby uzyskać wszystkie rozwiązane / odrzucone obiekty zwrócone przez tablicę deferreds, co jest brzydkie:

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

Ponieważ przekazaliśmy tablicę odroczonych, dobrze byłoby otrzymać tablicę wyników. Byłoby również miło odzyskać rzeczywistą tablicę zamiast pseudo-tablicy, abyśmy mogli użyć takich metod, jak Array.sort().

Oto rozwiązanie zainspirowane metodą when.js , when.all()które rozwiązuje te problemy:

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

Teraz możesz po prostu przekazać tablicę odroczonych / obietnic i odzyskać tablicę rozwiązanych / odrzuconych obiektów w swoim wywołaniu zwrotnym, na przykład:

$.when.all(promises).then(function(schemas) {
     console.log("DONE", this, schemas); // 'schemas' is now an array
}, function(e) {
     console.log("My ajax failed");
});
chrupiąca kaczka
źródło
@crispyduck - czy wiesz, czy możesz być w 100% pewien, że kolejność elementów tablicy w "schematach" var w then () będzie zawsze w tej samej kolejności, co wywołania ajax w "obietnicach" w zmiennej kiedy ()?
netpoetica
6
Powinno to być po prostu wbudowane w jQuery, ale - zespół jQuery kilkakrotnie odrzucał żądanie. W międzyczasie ludzie wciąż zadają to pytanie i otwierają podobne zgłoszenia przeciwko jQuery, a kończymy z wdrożeniem przestrzeni użytkownika wszędzie i / lub niezręcznymi wezwaniami do apply()...
mindplay.dk
Dzięki za to rozwiązanie! Czy istnieje sposób na zdobycie udanych przedmiotów również wtedy, gdy jeden (lub więcej) zawiódł?
doktoreas
cóż, wszystko co tu zrobiłeś, to ukryta argumentsmanipulacja we własnej metodzie. Świetne do ponownego wykorzystania, ale nie rozwiązuje problemu „brzydoty” konieczności radzenia sobie arguments(możesz z łatwością po prostu:var schemas=Array.prototype.slice.call(arguments);)
cowbert
2
@crispyduck, nie powinieneś deferred.fail(...)czytać deferred.reject(...)?
Bob S,
19

Jeśli używasz javascript w wersji ES6 Istnieje operator spreadu (...), który konwertuje tablicę obiektów na argumenty oddzielone przecinkami.

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

Więcej o operatorze spreadu ES6 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator znajdziesz tutaj

pashaplus
źródło
1
Tak. Chociaż ci z nas, którzy używają Coffeescript lub jednego z jego potomków / naśladowców, mają już od jakiegoś czasu dostęp do tego operatora.
Elf Sternberg
0

rozszerza się, gdy z tym kodem:

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)
    }
}
ZADZWOŃ DO MNIE TZ
źródło