วัตถุรอการตัดบัญชีคืออะไร?


คำตอบ:


101

วัตถุรอการตัดบัญชี

ตั้งแต่ jQuery 1.5 อ็อบเจ็กต์ Deferred มีวิธีการลงทะเบียนการเรียกกลับหลายรายการในคิวการโทรกลับที่จัดการด้วยตนเองเรียกใช้คิวการเรียกกลับตามความเหมาะสมและถ่ายทอดสถานะความสำเร็จหรือความล้มเหลวของฟังก์ชันซิงโครนัสหรืออะซิงโครนัส

วิธีการรอการตัดบัญชี:

  • deferred.done ()
    • เพิ่มตัวจัดการที่จะถูกเรียกเมื่ออ็อบเจ็กต์รอการตัดบัญชีได้รับการแก้ไข
  • deferred.fail ()
    • เพิ่มตัวจัดการที่จะถูกเรียกเมื่ออ็อบเจ็กต์รอการตัดบัญชีถูกปฏิเสธ
  • deferred.isRejected ()
    • ตรวจสอบว่าวัตถุที่รอการตัดบัญชีถูกปฏิเสธหรือไม่
  • deferred.isResolved ()
    • พิจารณาว่าออบเจ็กต์รอการตัดบัญชีได้รับการแก้ไขหรือไม่
  • deferred.reject ()
    • ปฏิเสธออบเจ็กต์ที่รอการตัดบัญชีและเรียกใช้ failCallbacks ใด ๆ ด้วยอาร์กิวเมนต์ที่กำหนด
  • deferred.rejectWith ()
    • ปฏิเสธออบเจ็กต์ที่รอการตัดบัญชีและเรียกใช้ failCallbacks ใด ๆ ด้วยบริบทและอาร์กิวเมนต์ที่กำหนด
  • deferred.resolve ()
    • แก้ไขออบเจ็กต์ที่รอการตัดบัญชีและเรียกใช้ doneCallbacks ด้วย args ที่กำหนด
  • deferred.resolveWith ()
    • แก้ไขออบเจ็กต์ที่รอการตัดบัญชีและเรียกใช้ doneCallbacks ใด ๆ ด้วยบริบทและ args ที่กำหนด
  • deferred.then ()
    • เพิ่มตัวจัดการที่จะถูกเรียกเมื่ออ็อบเจ็กต์ Deferred ได้รับการแก้ไขหรือปฏิเสธ

รอการตัดบัญชีในการดำเนินการ:

$.get("test.php").done(
    function(){ alert("$.get succeeded"); }
);

$.get("test.php")
    .done(function(){ alert("$.get succeeded"); })
    .fail(function(){ alert("$.get failed!"); });

และดูเหมือนว่าการเรียกกลับเมธอด ajax () ที่มีอยู่สามารถถูกล่ามโซ่แทนที่จะประกาศในการตั้งค่า:

var jqxhr = $.ajax({ url: "example.php" })
    .success(function() { alert("success"); })
    .error(function() { alert("error"); })
    .complete(function() { alert("complete"); });

ตัวอย่างการทำงานจากบล็อกโพสต์ของ Eric Hynds : http://jsfiddle.net/ehynds/Mrqf8/


jqXHR

สำหรับ jQuery 1.5 วิธี $ .ajax () จะส่งคืนอ็อบเจ็กต์ jXHR ซึ่งเป็นส่วนเหนือของอ็อบเจ็กต์ XMLHTTPRequest สำหรับข้อมูลเพิ่มเติมโปรดดูส่วน jXHR ของรายการ $ .ajax


จากJQUERY 1.5 RELEASED :

วัตถุเบี่ยงเบน

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

นอกจากนี้คุณสามารถสร้างวัตถุรอการตัดบัญชีของคุณเองโดยใช้ jQuery.Deferred ที่เปิดเผย ข้อมูลเพิ่มเติมเกี่ยวกับ API นี้สามารถพบได้ในเอกสารของ Deferred Object

เอริค Hynds ได้เขียนขึ้นกวดวิชาที่ดีในการใช้ Deferreds ใน jQuery 1.5


19
กรุณาอธิบายเพิ่มเติม ฉันจะสร้าง Deferred Objects ที่กำหนดเองได้อย่างไร พวกเขาทำงานอย่างไร?
user113716

3
ที่จริงฉันจริงจัง นี่เป็นคำถามที่ดีเกี่ยวกับคุณลักษณะใหม่ล่าสุด ฉันไม่รู้ว่ามันทำงานอย่างไรและฉันคิดว่ามันจะดีถ้า StackOverflow มีคำถามนี้อธิบายได้ดีสำหรับผู้ที่จะถามเกี่ยวกับเรื่องนี้ในอนาคต
user113716

1
อัปเดต: ฉันคิดว่าคำจำกัดความของ "รอการตัดบัญชี" ด้านบนที่ฉันเพิ่มไว้ด้านบนจะช่วยให้เห็นภาพที่ชัดเจนยิ่งขึ้นว่ามันกำลังทำอะไรอยู่ ดูเหมือนว่าจะสามารถเชื่อมโยงการโทรกลับได้มากกว่าที่จะประกาศในการตั้งค่าที่ส่งผ่านไปยังฟังก์ชัน
นักล่า

1
@Hunter ฉันต้องการคำอธิบายเกี่ยวกับวิธีการทำงาน คำถามนี้เป็นคำถามแรกดังนั้นตอบให้ดี!
Raynos

2
มีประโยชน์หลักบางประการ: ความสามารถในการแยกผลลัพธ์ของงาน async ที่เป็นไปได้, ความสามารถในการผูกตัวจัดการหลายประเภทที่แตกต่างกัน, การผูกตัวจัดการกับงานแม้ว่างานจะได้รับการแก้ไขแล้วก็ตาม, ผูกผลลัพธ์ของคำขอ async หลายรายการ ร่วมกันเพิ่มตัวจัดการตามเงื่อนไข ฯลฯ
สิ้นสุด

13

แต่จะบอกคุณว่ามันทำอะไรฉันจะแสดงให้คุณเห็นว่ามันทำอะไรและอธิบายมัน

สำเนาแหล่งที่มาที่เกี่ยวข้องของ jQuery 1.5 พร้อมคำอธิบายประกอบที่อธิบายว่ามันทำอะไร ผมว่าความคิดเห็นส่วนใหญ่ถูกต้อง

นี่อาจเป็นประโยชน์

// promiseMethods. These are the methods you get when you ask for a promise.
// A promise is a "read-only" version
// fullMethods = "then done fail resolve resolveWith reject rejectWith isResolve    isRejected promise cancel".split(" ")
// As you can see it removes resolve/reject so you can't actaully trigger a
// anything on the deferred object, only process callbacks when it "finishes".
promiseMethods = "then done fail isResolved isRejected promise".split(" "),

// Create a simple deferred (one callbacks list)
/* Class: _Deferred.
 *  methods: done, resolve, resolveWith, isResolved
 *  internal method: cancel
 *
 *  Basically allows you to attach callbacks with the done method.
 *  Then resolve the deferred action whenever you want with an argument.
 *  All the callbacks added with done will be called with the resolved argument
 *  Any callbacks attached after resolvement will fire immediatly.
 *
 *  resolveWith allows you to set the this scope in the callbacks fired.
 *
 *  isResolved just checks whether it's resolved yet.
 *
 *  cancel blocks resolve/resolveWith from firing. the methods added throug
 *  done will never be called
 */
_Deferred: function () {
    var // callbacks list
    callbacks = [],
        // stored [ context , args ]
        // stores the context & args that .resolve was called with
        fired,
        // to avoid firing when already doing so
        firing,
        // flag to know if the deferred has been cancelled
        // in Deferred cancel gets called after the first resolve call
        cancelled,
        // the deferred itself
        deferred = {

            // done( f1, f2, ...)
            done: function () {
                if (!cancelled) {
                    var args = arguments,
                        i, length,
                        // elem in callback list
                        elem,
                        // type of elem in callback list
                        type,
                        // cached context & args for when done is called
                        // after resolve has been
                        _fired;
                    // If resolve has been called already
                    if (fired) {
                        // mark it locally
                        _fired = fired;
                        // set fired to 0. This is neccesary to handle
                        // how done deals with arrays recursively
                        // only the original .done call handles fired
                        // any that unwrap arrays and call recursively
                        // dont handle the fired.
                        fired = 0;
                    }
                    // for each function append it to the callback list
                    for (i = 0, length = args.length; i < length; i++) {
                        elem = args[i];
                        type = jQuery.type(elem);
                        // if argument is an array then call done recursively
                        // effectively unwraps the array
                        if (type === "array") {
                            // def.done([f1, f2, f3]) goes to
                            // def.done(f1, f2, f3) through the apply
                            deferred.done.apply(deferred, elem);
                        } else if (type === "function") {
                            // if its a function add it to the callbacks
                            callbacks.push(elem);
                        }
                    }
                    // if it's already been resolved then call resolveWith using
                    // the cahced context and arguments to call the callbacks
                    // immediatly
                    if (_fired) {
                        deferred.resolveWith(_fired[0], _fired[1]);
                    }
                }
                return this;
            },

            // resolve with given context and args
            resolveWith: function (context, args) {
                                // if its been cancelled then we can't resolve
                                // if it has fired then we can't fire again
                                // if it's currently firing then we can't fire. This check is
                // there because of the try finally block. It ensures we
                // cant call resolve between the try & finally in the catch phase.
                if (!cancelled && !fired && !firing) {
                    firing = 1;
                    // try block because your calling external callbacks
                    // made by the user which are not bugfree.
                                        // the finally block will always run no matter how bad
                                        // the internal code is.
                    try {
                        while (callbacks[0]) {
                            callbacks.shift().apply(context, args);
                        }
                                        // cache the content and arguments taht have been called
                                        // and set firing to false.
                    } finally {
                        fired = [context, args];
                        firing = 0;
                    }
                }
                return this;
            },

            // resolve with this as context and given arguments
            // just maps to resolveWith, this sets the this scope as normal
            // maps to this.promise which is the read only version of Deferred.
            resolve: function () {
                deferred.resolveWith(jQuery.isFunction(this.promise) ? this.promise() : 
this, arguments);
                return this;
            },

            // Has this deferred been resolved?
            // checks whether it's firing or if it has fired.
            isResolved: function () {
                return !!(firing || fired);
            },

            // Cancels the action. To be used internally
            cancel: function () {
                cancelled = 1;
                callbacks = [];
                return this;
            }
        };

    return deferred;
},
/* Class: Deferred.
 *  methods: then, done, fail, resolve, reject, resolveWith, rejectWith, isResolved, 
isRejected, promise
 *
 *  then is a shortcut for both assigning done & fail in one function.
 *
 *  This one has two underlying lists with different semantic meanings. You
 *  can bind to both the done callbacks and the fail callbacks then either
 *  resolve or reject your Deferred object.
 *
 *  You can check whether it has been resolved or rejected. useful to see
 *  Afterwards which one has happened.
 *
 *  Call .promise to return a new object which doesn't have the resolve/reject
 *  methods on it. This means you can only bind to it and not resolve/reject it.
 *  This is effectively read-only.
 *
 */
// Full fledged deferred (two callbacks list)
Deferred: function (func) {
        // the main deferred which deals with the success callbacks
    var deferred = jQuery._Deferred(),
                // the failure deferred which deals with the rejected callbacks
        failDeferred = jQuery._Deferred(),
                // the read only promise is cached.
        promise;
    // Add errorDeferred methods, then and promise
    jQuery.extend(deferred, {
                // def.then([f1, f2, ...], [g1, g2, ...] is a short hand for
                // def.done([f1, f2, ...])
        // def.fail([g1, g2, ...])
        then: function (doneCallbacks, failCallbacks) {
                        // fail exists here because this code will only run after
                        // deferred has been extended.
            deferred.done(doneCallbacks).fail(failCallbacks);
            return this;
        },
                // map def.fail to the second underlying deferred callback list
                // map all the other methods for rejection/failure to the underlying
                // failDeffered object so that Deferred has two callback lists stored
                // internally.
        fail: failDeferred.done,
        rejectWith: failDeferred.resolveWith,
        reject: failDeferred.resolve,
        isRejected: failDeferred.isResolved,
        // Get a promise for this deferred
        // If obj is provided, the promise aspect is added to the object
                // no clue what to do with "i"
        promise: function (obj, i /* internal */ ) {
                        // if no argument is passed then just extend promise
            if (obj == null) {
                                // if cached return the cache.
                if (promise) {
                    return promise;
                }
                                // set promise & arg to be {}
                promise = obj = {};
            }
                        // for each promiseMethods in the read only promise list
            i = promiseMethods.length;
            while (i--) {
                                // set the deferred method on the object
                obj[promiseMethods[i]] = deferred[promiseMethods[i]];
            }
                        // returns the "read-only" deferred without
                        // resolve, resolveWith, reject & rejectWith.
                        // So you cant "resolve" it but only add "done" functions
            return obj;
        }
    });
    // Make sure only one callback list will be used
        // if either resolve or reject is called cancel both.
        // this means that the one that has been called cant be called again
        // and the other one will never be called. So only the done or the fail
        // methods will ever be called
    deferred.then(failDeferred.cancel, deferred.cancel);
        // Don't mess with cancel!
    // Unexpose cancel
    delete deferred.cancel;
    // Call given func if any
        // function argument to be called. This was passed in. Allows you to
        // handle the deferred object after creating a new one, both as this scope
        // and as a new argument.
    if (func) {
        func.call(deferred, deferred);
    }
    return deferred;
},

/* Method: when
 * Arguments: none OR 1 of type(any & !deferred) OR n of type(deferred).
 *
 * If no arguments are passed then it gets resolved immediatly. A good way to
 * call multiple callback functions? Don't really know a good use of $.when()
 *
 * If one argument is passed and its not a deferred object then it resolves
 * immediatly and passes that argument to all the done callbacks attached.
 *
 * if n arguments are passed of type deferred object then the the done callbacks
 * will only fire if all of them succeed. If a single one fails then the
 * fail callbacks fire.
 *
 * Returns a promise read-only deferred object
 */
// Deferred helper
when: function (object) {
    var args = arguments,
        length = args.length,
                // If you pass in a deferred object then set deferred to be the promise
        // if you pass in anything else then set deferred to be a new deferred
        deferred = length <= 1 && object && jQuery.isFunction(object.promise) ?
                object :
                        jQuery.Deferred(),
        // cache the promise
        promise = deferred.promise(),
                // store an array
        resolveArray;

        // if multiple objects are passed in
    if (length > 1) {
                // create an arrey to store of values.
        resolveArray = new Array(length);
                // for each object that we wait on
        jQuery.each(args, function (index, element) {
                        // when that object resolves then
            jQuery.when(element).then(function (value) {
                                // store value in the array or store an array of values in it
                resolveArray[index] = arguments.length > 1 ? slice.call(arguments, 0) : 
value;
                                // if length === 1 then we finished calling them all
                if (!--length) {
                                        // resolve the deferred object with the read only promise
                                        // as context and the resolved values array as the argument
                    deferred.resolveWith(promise, resolveArray);
                }
                        // if any fail then we reject or deferred
            }, deferred.reject);
        });
        // if deferred was newly created but there was only one argument then
    // resolve it immediatly with the argument.
    } else if (deferred !== object) {
        deferred.resolve(object);
    }
        // return the read-only deferred.
    return promise;
},

6
สิ่งนี้จะอ่านได้ดีกว่ามากถ้าคุณไม่มีแถบเลื่อนแนวนอน: /
gnarf

@gnarf Problem solved. Btw นั่นคือแหล่งที่มา 1.5beta ฉันคิดว่ามีการเปลี่ยนแปลงบางอย่างใน 1.6
Raynos

9

แก้ไขฉันถ้าฉันผิด แต่เมื่อไม่นานมานี้ฉันคลิกว่ามันเป็น Asynchronous Task Runner สัญญาเป็นสัญญาผลลัพธ์เพื่อให้แน่ใจว่าคุณจะได้รับ ... บางอย่าง แต่ไม่มีการรับประกันว่าคุณจะได้รับเมื่อใด


ดังนั้นไวน์เก่าในขวดใหม่!
ankush981

3

ในขณะที่ทำงานใน Javascript เราพบสถานการณ์ที่การเรียกใช้ฟังก์ชันเป็นแบบอะซิงโครนัส นั่นคือโฟลว์ของฟังก์ชัน calee (สมมติว่า X) ไม่รอให้เรียกว่าฟังก์ชันอะซิงโครนัส (สมมติว่า Y) ตัวอย่างทั่วไปคือเมื่อเราโทรไปยังเซิร์ฟเวอร์เพื่อดึงข้อมูลบางส่วนจากฐานข้อมูลหรือเพจ HTML หากการโทรเหล่านั้นไม่ใช่แบบอะซิงโครนัสอินเทอร์เฟซผู้ใช้จะค้างรอให้เซิร์ฟเวอร์ตอบกลับ ลักษณะอะซิงโครนัสนี้ทำให้เกิดปัญหาเมื่อคุณต้องการดำเนินการตามลำดับตัวอย่างเช่นคุณต้องการพิมพ์บางสิ่งหลังจาก Y (asynch) ดำเนินการเสร็จสิ้นหรือดึงข้อมูลเสร็จแล้ว ที่นี่ jQuery ให้ Deffered Object แก่เรา โดยพื้นฐานแล้ว jQuery ได้ดูแลโค้ดสำเร็จรูปทั้งหมดซึ่งโดยปกติเราจะเขียนเพื่อแก้ไขสถานการณ์นี้ นี่คือตัวอย่างง่ายๆ:

  $.ajax({
      ...
  }).done(function(){
      //write here what you wish to do when this ajax call is success
  }).fail(function(){
      //write here what you wish to do on failure of this ajax call
  }); //see more on jQuery Deferred page

คุณสามารถเขียนฟังก์ชันรอการตัดบัญชี (asynch) ของคุณเองได้

function DoSomethingTimeConsumingAsynch(){
    var deferred = $.Deferred();

    _.defer(function(){ //I am using underscore, you can also use setTimeout
        ...  
        deferred.resolve();//When the process is done successfully 
        ...
        deferred.reject(); //When the process has failed
    });
    return deferred;
}

//HEre how to use your own asynch function
DoSomethingTimeConsumingAsynch()
.done(function(){
   //this will be invoked on success
})
.fail(function(){
   //this will be invoked on failure
})

ฉันหวังว่านี่จะช่วยได้

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