คุณทำงานกับอาร์เรย์ของ jQuery Deferreds อย่างไร


132

ฉันมีแอปพลิเคชันที่ต้องโหลดข้อมูลตามลำดับที่กำหนด: URL รูทจากนั้นสคีมาจากนั้นจึงเริ่มต้นแอปพลิเคชันด้วยสกีมาและ URL สำหรับวัตถุข้อมูลต่างๆ ในขณะที่ผู้ใช้สำรวจแอปพลิเคชันอ็อบเจ็กต์ข้อมูลจะถูกโหลดตรวจสอบความถูกต้องกับสคีมาและแสดง เนื่องจากผู้ใช้ CRUD ข้อมูลสคีมาจะให้การตรวจสอบความถูกต้องก่อน

ฉันมีปัญหากับการเริ่มต้น ฉันใช้การเรียก Ajax เพื่อดึงอ็อบเจ็กต์รูท $. เมื่อ () จากนั้นสร้างอาร์เรย์ของสัญญาหนึ่งรายการสำหรับแต่ละออบเจ็กต์สคีมา ที่ได้ผล ฉันเห็นการดึงข้อมูลในคอนโซล

จากนั้นฉันเห็นการดึงข้อมูลสำหรับสคีมาทั้งหมดดังนั้นการเรียก $ .ajax () แต่ละครั้งจึงใช้ได้ fetchschemas () ส่งคืนอาร์เรย์ของสัญญา

อย่างไรก็ตามสุดท้ายเมื่อประโยค () ไม่เคยเริ่มทำงานและคำว่า "DONE" จะไม่ปรากฏบนคอนโซล ซอร์สโค้ดไปยัง jquery-1.5 ดูเหมือนจะบอกเป็นนัยว่า "null" เป็นที่ยอมรับในฐานะอ็อบเจกต์ที่จะส่งผ่านไปยัง $ .when.apply () เนื่องจากเมื่อ () จะสร้างอ็อบเจ็กต์ Deferred () ภายในเพื่อจัดการรายการหากไม่มีอ็อบเจ็กต์ ผ่านเข้ามา

สิ่งนี้ใช้ได้กับ Futures.js อาร์เรย์ของ jQuery Deferreds ควรได้รับการจัดการอย่างไรหากไม่เป็นเช่นนี้

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

ฉันเกือบจะมีปัญหาเหมือนกันยกเว้นว่าฉันต้องเริ่มการทำงานของเมธอด "สำเร็จ" สำหรับการค้นหา ajax แต่ละรายการใน fetch_one ก่อนที่จะพิมพ์ "DONE" คุณจะทำสิ่งนี้ได้อย่างไร? ฉันลองใช้. pip ต่อจาก "fetch_one" แต่ดูเหมือนจะไม่ได้ผล
CambridgeMike

คำตอบ:


198

คุณกำลังมองหา

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

สิ่งนี้จะใช้งานได้เช่นกัน (สำหรับมูลค่าของงานจะไม่สามารถแก้ไข ajax ที่เสียได้):

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

คุณจะต้องการที่จะผ่าน$แทนnullเพื่อให้thisภายในหมายถึง$.when jQueryมันไม่ควรมีความสำคัญกับแหล่งที่มา nullแต่ดีขึ้นแล้วผ่าน

เยาะเย้ย $ .ajax ของคุณทั้งหมดโดยแทนที่ด้วย$.whenและตัวอย่างใช้งานได้

ดังนั้นจึงเป็นปัญหาในคำขอ ajax ของคุณหรืออาร์เรย์ที่คุณส่งไปยัง fetch_schemas


ขอบคุณ. ไวยากรณ์นี้แตกต่างจาก done (). fail () อย่างไร?
Elf Sternberg

2
@elf Sternberg .then(a,b) === .done(a).fail(b)มันเป็นชวเลขที่ขี้เกียจ คุณสามารถโทรได้.done(a).fail(b)หากต้องการ
Raynos

1
โอ้และการใช้ $ .when.apply ($, ... ) และ $ .when.apply (null, ... ) ดูเหมือนจะไม่เกี่ยวข้องกัน jQuery เองไม่มีวิธีการคำมั่นสัญญา () ดังนั้นจึงถูกเพิกเฉยต่อวัตถุรอการตัดบัญชีที่สร้างขึ้นภายใน (jQuery 1.5 บรรทัด 943)
Elf Sternberg

1
@ElfSternberg มันจะไม่เกี่ยวข้องแน่นอน $.when.apply($, ...แต่สำหรับการอ่านฉันไม่จำเป็นต้องใช้เวลาอย่างรวดเร็วที่สองที่ nullทำให้ฉันไป "รออะไร?" มันเป็นเรื่องของรูปแบบและการฝึกเขียนโค้ด ฉันต้องอ่านแหล่งที่มาเพื่อยืนยันว่าthisจะไม่โยนการอ้างอิงว่างใน jQuery.when!
Raynos

7
การใช้ null ทำให้ฉันคิดว่า 'โอเคนี่เป็นวิธีแก้ปัญหาแบบหนึ่ง' (ซึ่งก็คือ) ในขณะที่ $ ถูกใช้ความสนใจของฉันจะถูกเปลี่ยนไปคิดเกี่ยวกับ wtf ที่ $ สำหรับ
Danyal Aytekin

53

วิธีแก้ปัญหาข้างต้น (ขอบคุณ!) ไม่ได้แก้ไขปัญหาในการรับคืนอ็อบเจ็กต์ที่ให้ไว้ในresolve()เมธอดของการเลื่อนการตัดบัญชีอย่างถูกต้องเนื่องจาก jQuery เรียกใช้done()และfail()เรียกกลับด้วยพารามิเตอร์แต่ละตัวไม่ใช่อาร์เรย์ นั่นหมายความว่าเราต้องใช้argumentsอาร์เรย์หลอกเพื่อรับอ็อบเจ็กต์ที่ได้รับการแก้ไข / ปฏิเสธทั้งหมดที่ส่งคืนโดยอาร์เรย์ของการรอการตัดบัญชีซึ่งน่าเกลียด:

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

เนื่องจากเราผ่านอาร์เรย์ของการรอการตัดบัญชีจึงเป็นการดีที่จะได้ผลลัพธ์กลับมา Array.sort()นอกจากนี้ยังจะดีที่จะได้รับกลับอาร์เรย์ที่เกิดขึ้นจริงแทนการหลอกอาร์เรย์เพื่อให้เราสามารถใช้วิธีการเช่น

นี่คือวิธีแก้ปัญหาที่ได้รับแรงบันดาลใจจากเมธอดของwhen.jswhen.all()ที่แก้ไขปัญหาเหล่านี้:

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

ตอนนี้คุณสามารถส่งผ่านอาร์เรย์ของการรอการตัดบัญชี / สัญญาและรับอาร์เรย์ของวัตถุที่แก้ไข / ปฏิเสธกลับมาในการเรียกกลับของคุณดังนี้:

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

@crispyduck - คุณรู้หรือไม่ว่าคุณมั่นใจได้ 100% หรือไม่ว่าลำดับขององค์ประกอบอาร์เรย์ใน var ใน "schemas" จากนั้น () จะอยู่ในลำดับเดียวกับการเรียก ajax ใน "สัญญา" ในเวลาที่ ()?
netpoetica

6
สิ่งนี้ควรจะเป็นเพียงแค่ jQuery ในตัว แต่ - ทีม jQuery ปฏิเสธคำขอหลายครั้ง ในขณะเดียวกันผู้คนยังคงถามคำถามที่นี่และเปิดตั๋วที่คล้ายกันกับ jQuery และเราจบลงด้วยการใช้งาน userland ทุกที่และ / หรือเรียกร้องให้apply()...
mindplay.dk

ขอบคุณสำหรับการแก้ปัญหานี้! มีวิธีรับไอเท็มสำเร็จหรือไม่หากหนึ่ง (หรือมากกว่า) ล้มเหลว?
doktoreas

สิ่งที่คุณทำที่นี่คือการargumentsจัดการที่ซ่อนอยู่ในวิธีการของมันเอง เหมาะอย่างยิ่งสำหรับการใช้ซ้ำ แต่ไม่ได้กล่าวถึง "ความอัปลักษณ์" ที่ต้องจัดการarguments(คุณสามารถทำได้ง่ายๆเพียง:var schemas=Array.prototype.slice.call(arguments);)
cowbert

2
@crispyduck ไม่ควรdeferred.fail(...)อ่านdeferred.reject(...)?
Bob S

19

หากคุณใช้จาวาสคริปต์เวอร์ชัน ES6 จะมีตัวดำเนินการกระจาย (... ) ซึ่งแปลงอาร์เรย์ของอ็อบเจ็กต์เป็นอาร์กิวเมนต์ที่คั่นด้วยเครื่องหมายจุลภาค

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

ข้อมูลเพิ่มเติมเกี่ยวกับ ES6 spread operator https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operatorดูได้ที่นี่


1
ได้. แม้ว่าพวกเราที่ใช้ Coffeescript หรือหนึ่งในผู้สืบทอด / ผู้ลอกเลียนแบบจะสามารถเข้าถึงโอเปอเรเตอร์นั้นได้สักพักแล้ว
Elf Sternberg

0

ขยายเมื่อมีรหัสนี้:

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)
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.