Bagaimana Anda bekerja dengan array jQuery Deferreds?

132

Saya memiliki aplikasi yang memerlukan data dimuat dalam urutan tertentu: URL root, lalu skema, lalu akhirnya menginisialisasi aplikasi dengan skema dan url untuk berbagai objek data. Saat pengguna menavigasi aplikasi, objek data dimuat, divalidasi terhadap skema, dan ditampilkan. Sebagai pengguna CRUD data, skema menyediakan validasi first-pass.

Saya mengalami masalah dengan inisialisasi. Saya menggunakan panggilan Ajax untuk mengambil objek root, $ .when (), dan kemudian membuat array janji, satu untuk setiap objek skema. Itu bekerja. Saya melihat pengambilan di konsol.

Saya kemudian melihat pengambilan untuk semua skema, sehingga setiap panggilan $ .ajax () berfungsi. fetchschemas () memang mengembalikan sejumlah janji.

Namun, kalimat terakhir ketika () klausa tidak pernah menyala dan kata "DONE" tidak pernah muncul di konsol. Kode sumber ke jquery-1.5 tampaknya menyiratkan bahwa "null" dapat diterima sebagai objek untuk diteruskan ke $ .when.apply (), seperti ketika () akan membangun objek internal Deferred () untuk mengelola daftar jika tidak ada objek yang lewat.

Ini berfungsi menggunakan Futures.js. Bagaimana seharusnya array jQuery Deferreds dikelola, jika tidak seperti ini?

    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
sumber
Saya memiliki masalah yang hampir sama, kecuali saya harus menjalankan metode "sukses" untuk setiap permintaan ajax di fetch_one, sebelum "DONE" dicetak. Bagaimana Anda akan melakukan ini? Saya mencoba menggunakan .pipe setelah "fetch_one", tetapi sepertinya itu tidak berhasil.
CambridgeMike

Jawaban:

198

Kamu sedang mencari

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

Ini juga akan berfungsi (untuk beberapa nilai pekerjaan, itu tidak akan memperbaiki patah ajax):

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

Anda ingin lulus $alih-alih nullagar thisbagian dalam $.whenmengacu jQuery. Seharusnya tidak masalah untuk sumbernya, tetapi lebih baik daripada lewat null.

Mengejek semua $ .ajax Anda dengan menggantinya dengan $.whendan sampel berfungsi

Jadi itu bisa menjadi masalah dalam permintaan ajax Anda atau array yang Anda lewati untuk fetch_schemas.

Raynos
sumber
Terima kasih. Bagaimana sintaks ini berbeda dari sintaks selesai (). Gagal ()?
Elf Sternberg
2
@elf Sternberg, .then(a,b) === .done(a).fail(b)ini tulisan cepat yang malas. Anda dapat menelepon .done(a).fail(b)jika Anda mau
Raynos
1
Oh, dan penggunaan $ .when.apply ($, ...) dan $ .when.apply (null, ...) tampaknya tidak relevan. jQuery itu sendiri tidak memiliki metode janji (), sehingga diabaikan karena mendukung objek yang ditangguhkan secara internal (jQuery 1.5, baris 943).
Elf Sternberg
1
@ ElfSternberg memang tidak relevan, tetapi untuk keterbacaan saya tidak perlu mengambil pandangan kedua $.when.apply($, .... The nullmembuat saya pergi "menunggu, apa?". Ini masalah gaya dan praktik pengkodean. Saya harus membaca sumber untuk mengkonfirmasi thistidak akan membuang referensi nol di dalam jQuery.when!
Raynos
7
Penggunaan null membuat saya berpikir 'ok, ini semacam solusi' (yang artinya), sedangkan jika $ digunakan, perhatian saya akan dialihkan untuk berpikir tentang $ untuk apa.
Danyal Aytekin
53

Solusi di atas (terima kasih!) Tidak dengan benar mengatasi masalah untuk mendapatkan kembali objek yang disediakan untuk metode yang ditangguhkan resolve()karena jQuery memanggil done()dan memanggil kembali fail()dengan parameter individual, bukan array. Itu berarti kita harus menggunakan argumentspseudo-array untuk mendapatkan semua objek yang diselesaikan / ditolak dikembalikan oleh array yang ditangguhkan, yang jelek:

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

Karena kami melewati dalam array yang ditangguhkan, alangkah baiknya untuk mendapatkan kembali array hasil. Akan lebih baik untuk mendapatkan kembali array aktual alih-alih pseudo-array sehingga kita dapat menggunakan metode seperti Array.sort().

Berikut ini adalah solusi terinspirasi oleh when.js 's when.all()metode yang alamat masalah ini:

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

Sekarang Anda dapat dengan mudah memasukkan array dari ditangguhkan / janji dan mendapatkan kembali array objek yang diselesaikan / ditolak dalam panggilan balik Anda, seperti:

$.when.all(promises).then(function(schemas) {
     console.log("DONE", this, schemas); // 'schemas' is now an array
}, function(e) {
     console.log("My ajax failed");
});
crispyduck
sumber
@crispyduck - tahukah Anda jika Anda dapat 100% yakin bahwa urutan elemen array di "schemas" var di maka () akan selalu berada dalam urutan yang sama dengan panggilan ajax di "janji" var di saat ()?
netpoetica
6
Ini seharusnya hanya menjadi jQuery, tetapi - tim jQuery telah menolak permintaan tersebut beberapa kali. Sementara itu, orang-orang terus mengajukan pertanyaan di sini dan membuka tiket serupa terhadap jQuery dan kami berakhir dengan implementasi userland di mana-mana dan / atau panggilan canggung untuk apply()... mencari tahu.
mindplay.dk
Terima kasih atas solusi ini! Apakah ada cara untuk mendapatkan barang yang berhasil juga jika satu (atau lebih) gagal?
doktoreas
semua yang Anda lakukan di sini adalah argumentsmanipulasi tersembunyi ke dalam metode sendiri. Bagus untuk digunakan kembali, tetapi tidak membahas "keburukan" karena harus berurusan dengan arguments(Anda bisa dengan mudah hanya:var schemas=Array.prototype.slice.call(arguments);)
cowbert
2
@crispyduck, seharusnya tidak deferred.fail(...)membaca deferred.reject(...)?
Bob S
19

Jika Anda menggunakan versi ES6 dari javascript Ada operator spread (...) yang mengubah array objek menjadi argumen yang dipisahkan koma.

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

Lebih lanjut tentang operator penyebaran ES6 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator temukan di sini

pashaplus
sumber
1
Ya. Meskipun kami yang menggunakan Coffeescript atau salah satu keturunannya / peniru telah memiliki akses ke operator untuk sementara waktu sekarang.
Elf Sternberg
0

meluas ketika dengan kode ini:

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)
    }
}
HUBUNGI SAYA TZ
sumber